diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml index ab787cf3790bb65471200edaac706240b98a98fa..b9119cccb8a6038533511705991012eb757ec989 100644 --- a/briar-android/AndroidManifest.xml +++ b/briar-android/AndroidManifest.xml @@ -12,6 +12,9 @@ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.VIBRATE" /> + <!-- FIXME: Only needed for alpha and beta builds --> + <uses-permission android:name="android.permission.READ_LOGS" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application android:theme="@style/LightTheme" @@ -54,6 +57,11 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + <activity + android:name=".android.TestingActivity" + android:logo="@drawable/logo" + android:label="@string/app_name" > + </activity> <activity android:name=".android.contact.ContactListActivity" android:logo="@drawable/logo" diff --git a/briar-android/res/drawable-hdpi/action_help.png b/briar-android/res/drawable-hdpi/action_help.png new file mode 100644 index 0000000000000000000000000000000000000000..459bed76c5b9f546541383271a6c9b9ff9943093 Binary files /dev/null and b/briar-android/res/drawable-hdpi/action_help.png differ diff --git a/briar-android/res/drawable-hdpi/social_share.png b/briar-android/res/drawable-hdpi/social_share.png new file mode 100644 index 0000000000000000000000000000000000000000..47ae186749df92bbbe77f860ce657d3edce27765 Binary files /dev/null and b/briar-android/res/drawable-hdpi/social_share.png differ diff --git a/briar-android/res/drawable-mdpi/action_help.png b/briar-android/res/drawable-mdpi/action_help.png new file mode 100644 index 0000000000000000000000000000000000000000..72edd5a761481a3598974dac6d741b31ca953fb7 Binary files /dev/null and b/briar-android/res/drawable-mdpi/action_help.png differ diff --git a/briar-android/res/drawable-mdpi/social_share.png b/briar-android/res/drawable-mdpi/social_share.png new file mode 100644 index 0000000000000000000000000000000000000000..8aa52bc7d8a74739dc5e0ad9c35a006bdcbda182 Binary files /dev/null and b/briar-android/res/drawable-mdpi/social_share.png differ diff --git a/briar-android/res/drawable-xhdpi/action_help.png b/briar-android/res/drawable-xhdpi/action_help.png new file mode 100644 index 0000000000000000000000000000000000000000..0e67d7c12130fd86e9c7191c236b31aebdfe890e Binary files /dev/null and b/briar-android/res/drawable-xhdpi/action_help.png differ diff --git a/briar-android/res/drawable-xhdpi/social_share.png b/briar-android/res/drawable-xhdpi/social_share.png new file mode 100644 index 0000000000000000000000000000000000000000..cdafd8abca1912ff47f812c38fd29994da1d7867 Binary files /dev/null and b/briar-android/res/drawable-xhdpi/social_share.png differ diff --git a/briar-android/res/values/color.xml b/briar-android/res/values/color.xml index b25cc1843523f87d764dddef7c035f665adb81a1..8b0ec7923ca707bd4eaa5f1bfca10cce4f4825cf 100644 --- a/briar-android/res/values/color.xml +++ b/briar-android/res/values/color.xml @@ -4,6 +4,7 @@ <color name="action_bar_text">#FFFFFF</color> <color name="action_bar_background">#2D3E50</color> <color name="button_bar_background">#FFFFFF</color> + <color name="dashboard_background">#FFFFFF</color> <color name="private_message_background">#FFFFFF</color> <color name="private_message_date">#AAAAAA</color> <color name="unread_background">#FFFFFF</color> diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml index 338a01086517004095cfe475453d8f5c57a07837..b452b23ea3e3e03631824fbc47c21abb92948b74 100644 --- a/briar-android/res/values/strings.xml +++ b/briar-android/res/values/strings.xml @@ -16,7 +16,7 @@ <string name="expiry_warning">This software has expired.\nPlease install a newer version.</string> <string name="contact_list_button">Contacts</string> <string name="forums_button">Forums</string> - <string name="synchronize_button">Synchronize</string> + <string name="testing_button">Testing</string> <string name="sign_out_button">Sign Out</string> <string name="contact_list_title">Contacts</string> <string name="no_contacts">No contacts</string> diff --git a/briar-android/src/org/briarproject/android/DashboardActivity.java b/briar-android/src/org/briarproject/android/DashboardActivity.java index f1f604f1e764706b2134dd18ab24501580310054..956254c4f1e34ff302b0082af3fece087468d580 100644 --- a/briar-android/src/org/briarproject/android/DashboardActivity.java +++ b/briar-android/src/org/briarproject/android/DashboardActivity.java @@ -2,7 +2,6 @@ package org.briarproject.android; import static android.view.Gravity.CENTER; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.widget.Toast.LENGTH_SHORT; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static org.briarproject.android.util.CommonLayoutParams.MATCH_MATCH; @@ -37,7 +36,6 @@ import android.widget.GridView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ProgressBar; -import android.widget.Toast; public class DashboardActivity extends BriarActivity { @@ -121,20 +119,19 @@ public class DashboardActivity extends BriarActivity { }); buttons.add(forumsButton); - Button syncButton = new Button(this); - syncButton.setLayoutParams(matchMatch); - syncButton.setBackgroundResource(0); - syncButton.setCompoundDrawablesWithIntrinsicBounds(0, - R.drawable.navigation_refresh, 0, 0); - syncButton.setText(R.string.synchronize_button); - syncButton.setOnClickListener(new OnClickListener() { + Button testingButton = new Button(this); + testingButton.setLayoutParams(matchMatch); + testingButton.setBackgroundResource(0); + testingButton.setCompoundDrawablesWithIntrinsicBounds(0, + R.drawable.action_help, 0, 0); + testingButton.setText(R.string.testing_button); + testingButton.setOnClickListener(new OnClickListener() { public void onClick(View view) { - // FIXME: Hook this button up to an activity - Toast.makeText(DashboardActivity.this, - R.string.not_implemented_toast, LENGTH_SHORT).show(); + startActivity(new Intent(DashboardActivity.this, + TestingActivity.class)); } }); - buttons.add(syncButton); + buttons.add(testingButton); Button signOutButton = new Button(this); signOutButton.setLayoutParams(matchMatch); @@ -157,7 +154,7 @@ public class DashboardActivity extends BriarActivity { grid.setGravity(CENTER); grid.setPadding(pad, pad, pad, pad); Resources res = getResources(); - grid.setBackgroundColor(res.getColor(R.color.button_bar_background)); + grid.setBackgroundColor(res.getColor(R.color.dashboard_background)); grid.setNumColumns(2); grid.setAdapter(new BaseAdapter() { diff --git a/briar-android/src/org/briarproject/android/TestingActivity.java b/briar-android/src/org/briarproject/android/TestingActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..0ff9381ce9fad05d4941dab7fe979174cecf6522 --- /dev/null +++ b/briar-android/src/org/briarproject/android/TestingActivity.java @@ -0,0 +1,347 @@ +package org.briarproject.android; + +import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE; +import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE; +import static android.content.Intent.ACTION_SEND; +import static android.content.Intent.EXTRA_EMAIL; +import static android.content.Intent.EXTRA_STREAM; +import static android.content.Intent.EXTRA_SUBJECT; +import static android.net.ConnectivityManager.TYPE_MOBILE; +import static android.net.ConnectivityManager.TYPE_WIFI; +import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; +import static android.view.Gravity.CENTER; +import static android.view.Gravity.CENTER_HORIZONTAL; +import static android.widget.LinearLayout.VERTICAL; +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.WARNING; +import static org.briarproject.android.util.CommonLayoutParams.MATCH_MATCH; +import static org.briarproject.android.util.CommonLayoutParams.MATCH_WRAP; +import static org.briarproject.android.util.CommonLayoutParams.MATCH_WRAP_1; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Scanner; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.logging.Logger; + +import javax.inject.Inject; + +import org.briarproject.R; +import org.briarproject.android.util.ElasticHorizontalSpace; +import org.briarproject.android.util.HorizontalBorder; +import org.briarproject.android.util.LayoutUtils; +import org.briarproject.api.TransportId; +import org.briarproject.api.android.AndroidExecutor; +import org.briarproject.api.plugins.Plugin; +import org.briarproject.api.plugins.PluginManager; +import org.briarproject.util.StringUtils; + +import android.bluetooth.BluetoothAdapter; +import android.content.Intent; +import android.content.res.Resources; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.Uri; +import android.net.wifi.WifiManager; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Environment; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; + +public class TestingActivity extends BriarActivity implements OnClickListener { + + private static final Logger LOG = + Logger.getLogger(TestingActivity.class.getName()); + + @Inject private AndroidExecutor androidExecutor; + @Inject private PluginManager pluginManager; + private ScrollView scroll = null; + private LinearLayout status = null; + private ImageButton refresh = null, share = null; + private File temp = null; + + @Override + public void onCreate(Bundle state) { + super.onCreate(state); + + LinearLayout layout = new LinearLayout(this); + layout.setLayoutParams(MATCH_MATCH); + layout.setOrientation(VERTICAL); + layout.setGravity(CENTER_HORIZONTAL); + + scroll = new ScrollView(this); + scroll.setLayoutParams(MATCH_WRAP_1); + status = new LinearLayout(this); + status.setOrientation(VERTICAL); + status.setGravity(CENTER_HORIZONTAL); + int pad = LayoutUtils.getPadding(this); + status.setPadding(pad, pad, pad, pad); + scroll.addView(status); + layout.addView(scroll); + + layout.addView(new HorizontalBorder(this)); + + LinearLayout footer = new LinearLayout(this); + footer.setLayoutParams(MATCH_WRAP); + footer.setGravity(CENTER); + Resources res = getResources(); + footer.setBackgroundColor(res.getColor(R.color.button_bar_background)); + footer.addView(new ElasticHorizontalSpace(this)); + + refresh = new ImageButton(this); + refresh.setBackgroundResource(0); + refresh.setImageResource(R.drawable.navigation_refresh); + refresh.setOnClickListener(this); + footer.addView(refresh); + footer.addView(new ElasticHorizontalSpace(this)); + + share = new ImageButton(this); + share.setBackgroundResource(0); + share.setImageResource(R.drawable.social_share); + share.setOnClickListener(this); + footer.addView(share); + footer.addView(new ElasticHorizontalSpace(this)); + layout.addView(footer); + + setContentView(layout); + } + + @Override + public void onResume() { + super.onResume(); + refresh(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + if(temp != null) temp.delete(); + } + + public void onClick(View view) { + if(view == refresh) refresh(); + else if(view == share) share(); + } + + private void refresh() { + status.removeAllViews(); + new AsyncTask<Void, Void, Map<String, String>>() { + + protected Map<String, String> doInBackground(Void... args) { + return getStatusMap(); + } + + protected void onPostExecute(Map<String, String> result) { + int pad = LayoutUtils.getPadding(TestingActivity.this); + for(Entry<String, String> e : result.entrySet()) { + TextView title = new TextView(TestingActivity.this); + title.setTextSize(18); + title.setText(e.getKey()); + status.addView(title); + TextView content = new TextView(TestingActivity.this); + content.setPadding(0, 0, 0, pad); + content.setText(e.getValue()); + status.addView(content); + } + scroll.scrollTo(0, 0); + } + }.execute(); + } + + private Map<String, String> getStatusMap() { + Map<String, String> statusMap = new LinkedHashMap<String, String>(); + // Is mobile data available? + Object o = getSystemService(CONNECTIVITY_SERVICE); + ConnectivityManager cm = (ConnectivityManager) o; + NetworkInfo mobile = cm.getNetworkInfo(TYPE_MOBILE); + boolean mobileAvailable = mobile != null && mobile.isAvailable(); + // Is mobile data enabled? + boolean mobileEnabled = false; + try { + Class<?> clazz = Class.forName(cm.getClass().getName()); + Method method = clazz.getDeclaredMethod("getMobileDataEnabled"); + method.setAccessible(true); + mobileEnabled = (Boolean) method.invoke(cm); + } catch(ClassNotFoundException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + } catch(NoSuchMethodException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + } catch(IllegalAccessException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + } catch(IllegalArgumentException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + } catch(InvocationTargetException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + } + // Is mobile data connected ? + boolean mobileConnected = mobile != null && mobile.isConnected(); + + // Strings aren't loaded from resources as this activity is temporary + String mobileStatus; + if(mobileAvailable) mobileStatus = "Available, "; + else mobileStatus = "Not available, "; + if(mobileEnabled) mobileStatus += "enabled, "; + else mobileStatus += "not enabled, "; + if(mobileConnected) mobileStatus += "connected"; + else mobileStatus += "not connected"; + statusMap.put("Mobile data:", mobileStatus); + + // Is wifi available? + NetworkInfo wifi = cm.getNetworkInfo(TYPE_WIFI); + boolean wifiAvailable = wifi != null && wifi.isAvailable(); + // Is wifi enabled? + WifiManager wm = (WifiManager) getSystemService(WIFI_SERVICE); + boolean wifiEnabled = wm != null && + wm.getWifiState() == WIFI_STATE_ENABLED; + // Is wifi connected? + boolean wifiConnected = wifi != null && wifi.isConnected(); + + String wifiStatus; + if(wifiAvailable) wifiStatus = "Available, "; + else wifiStatus = "Not available, "; + if(wifiEnabled) wifiStatus += "enabled, "; + else wifiStatus += "not enabled, "; + if(wifiConnected) wifiStatus += "connected"; + else wifiStatus += "not connected"; + statusMap.put("Wi-Fi:", wifiStatus); + + // Is Bluetooth available? + BluetoothAdapter bt = null; + try { + bt = androidExecutor.call(new Callable<BluetoothAdapter>() { + public BluetoothAdapter call() throws Exception { + return BluetoothAdapter.getDefaultAdapter(); + } + }); + } catch(InterruptedException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + } catch(ExecutionException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + } + boolean btAvailable = bt != null; + // Is Bluetooth enabled? + boolean btEnabled = bt != null && bt.isEnabled() && + !StringUtils.isNullOrEmpty(bt.getAddress()); + // Is Bluetooth connectable? + boolean btConnectable = bt != null && + (bt.getScanMode() == SCAN_MODE_CONNECTABLE || + bt.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE); + // Is Bluetooth discoverable? + boolean btDiscoverable = bt != null && + bt.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE; + + String btStatus; + if(btAvailable) btStatus = "Available, "; + else btStatus = "Not available, "; + if(btEnabled) btStatus += "enabled, "; + else btStatus += "not enabled, "; + if(btConnectable) btStatus += "connectable, "; + else btStatus += "not connectable, "; + if(btDiscoverable) btStatus += "discoverable"; + else btStatus += "not discoverable"; + statusMap.put("Bluetooth:", btStatus); + + Plugin torPlugin = pluginManager.getPlugin(new TransportId("tor")); + boolean torPluginEnabled = torPlugin != null; + boolean torPluginRunning = torPlugin != null && torPlugin.isRunning(); + + String torPluginStatus; + if(torPluginEnabled) torPluginStatus = "Enabled, "; + else torPluginStatus = "Not enabled, "; + if(torPluginRunning) torPluginStatus += "running"; + else torPluginStatus += "not running"; + statusMap.put("Tor plugin:", torPluginStatus); + + Plugin lanPlugin = pluginManager.getPlugin(new TransportId("lan")); + boolean lanPluginEnabled = lanPlugin != null; + boolean lanPluginRunning = lanPlugin != null && lanPlugin.isRunning(); + + String lanPluginStatus; + if(lanPluginEnabled) lanPluginStatus = "Enabled, "; + else lanPluginStatus = "Not enabled, "; + if(lanPluginRunning) lanPluginStatus += "running"; + else lanPluginStatus += "not running"; + statusMap.put("LAN plugin:", lanPluginStatus); + + Plugin btPlugin = pluginManager.getPlugin(new TransportId("bt")); + boolean btPluginEnabled = btPlugin != null; + boolean btPluginRunning = btPlugin != null && btPlugin.isRunning(); + + String btPluginStatus; + if(btPluginEnabled) btPluginStatus = "Enabled, "; + else btPluginStatus = "Not enabled, "; + if(btPluginRunning) btPluginStatus += "running"; + else btPluginStatus += "not running"; + statusMap.put("Bluetooth plugin:", btPluginStatus); + + StringBuilder log = new StringBuilder(); + try { + Runtime runtime = Runtime.getRuntime(); + Process process = runtime.exec("logcat -d -s TorPlugin"); + Scanner scanner = new Scanner(process.getInputStream()); + while(scanner.hasNextLine()) { + log.append(scanner.nextLine()); + log.append('\n'); + } + scanner.close(); + } catch(IOException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + } + statusMap.put("Tor log:", log.toString()); + + return Collections.unmodifiableMap(statusMap); + } + + private void share() { + new AsyncTask<Void, Void, Map<String, String>>() { + + protected Map<String, String> doInBackground(Void... args) { + return getStatusMap(); + } + + protected void onPostExecute(Map<String, String> result) { + try { + File shared = Environment.getExternalStorageDirectory(); + temp = File.createTempFile("debug", "txt", shared); + if(LOG.isLoggable(INFO)) + LOG.info("Writing to " + temp.getPath()); + PrintStream p = new PrintStream(new FileOutputStream(temp)); + for(Entry<String, String> e : result.entrySet()) { + p.println(e.getKey()); + p.println(e.getValue()); + p.println(); + } + p.flush(); + p.close(); + sendEmail(Uri.fromFile(temp)); + } catch(IOException e) { + if(LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + } + } + }.execute(); + } + + private void sendEmail(Uri attachment) { + Intent i = new Intent(ACTION_SEND); + i.setType("message/rfc822"); + i.putExtra(EXTRA_EMAIL, new String[] { "debug@briarproject.org" }); + i.putExtra(EXTRA_SUBJECT, "Debugging information"); + i.putExtra(EXTRA_STREAM, attachment); + startActivity(Intent.createChooser(i, "Send to developers")); + } +} diff --git a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java index b2609f435d8718ceff9771235a6f972ad756a42c..4a875f5fb661eddfd8f34e15393adefcd3ef9a9e 100644 --- a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java +++ b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothPlugin.java @@ -68,6 +68,7 @@ class DroidtoothPlugin implements DuplexPlugin { private volatile boolean running = false; private volatile boolean wasEnabled = false, isEnabled = false; + private volatile BluetoothServerSocket socket = null; // Non-null if running has ever been true private volatile BluetoothAdapter adapter = null; @@ -149,7 +150,8 @@ class DroidtoothPlugin implements DuplexPlugin { tryToClose(ss); return; } - acceptContactConnections(ss); + socket = ss; + acceptContactConnections(); } private boolean enableBluetooth() { @@ -198,15 +200,15 @@ class DroidtoothPlugin implements DuplexPlugin { } } - private void acceptContactConnections(BluetoothServerSocket ss) { + private void acceptContactConnections() { while(true) { BluetoothSocket s; try { - s = ss.accept(); + s = socket.accept(); } catch(IOException e) { // This is expected when the socket is closed if(LOG.isLoggable(INFO)) LOG.log(INFO, e.toString(), e); - tryToClose(ss); + tryToClose(socket); return; } callback.incomingConnectionCreated(wrapSocket(s)); @@ -220,6 +222,7 @@ class DroidtoothPlugin implements DuplexPlugin { public void stop() { running = false; + if(socket != null) tryToClose(socket); // Disable Bluetooth if we enabled it at startup if(isEnabled && !wasEnabled) disableBluetooth(); } @@ -246,6 +249,10 @@ class DroidtoothPlugin implements DuplexPlugin { } } + public boolean isRunning() { + return running && socket != null; + } + public boolean shouldPoll() { return true; } diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java index ed526aee2db483a684c6c262824043402f6a009c..80ac29e959d5a1c7f1e892eb8d7b9bc31789fcda 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java @@ -72,7 +72,7 @@ class TorPlugin implements DuplexPlugin, EventHandler { private final File torDirectory, torFile, geoIpFile, configFile, doneFile; private final File cookieFile, pidFile, hostnameFile; - private volatile boolean running = false; + private volatile boolean running = false, networkEnabled = false; private volatile Process tor = null; private volatile int pid = -1; private volatile ServerSocket socket = null; @@ -509,6 +509,7 @@ class TorPlugin implements DuplexPlugin, EventHandler { if(!running) return; if(LOG.isLoggable(INFO)) LOG.info("Enabling network: " + enable); controlConnection.setConf("DisableNetwork", enable ? "0" : "1"); + networkEnabled = enable; } public void stop() throws IOException { @@ -534,6 +535,10 @@ class TorPlugin implements DuplexPlugin, EventHandler { } } + public boolean isRunning() { + return running && networkEnabled; + } + public boolean shouldPoll() { return true; } diff --git a/briar-api/src/org/briarproject/api/plugins/Plugin.java b/briar-api/src/org/briarproject/api/plugins/Plugin.java index 0eb66620ee8ff72d75bbd48b5bf67a0889d17995..3389a73577d9da1ce789a25d3387d2ae87fe9b16 100644 --- a/briar-api/src/org/briarproject/api/plugins/Plugin.java +++ b/briar-api/src/org/briarproject/api/plugins/Plugin.java @@ -23,6 +23,9 @@ public interface Plugin { /** Stops the plugin. */ void stop() throws IOException; + /** Returns true if the plugin is running. */ + boolean isRunning(); + /** * Returns true if the plugin's {@link #poll(Collection)} method should be * called periodically to attempt to establish connections. diff --git a/briar-api/src/org/briarproject/api/plugins/PluginManager.java b/briar-api/src/org/briarproject/api/plugins/PluginManager.java index 330de8669bab1285125bd59c169b843ca2c31bd6..efc336cf020c17686bd1441e50202828b437fecc 100644 --- a/briar-api/src/org/briarproject/api/plugins/PluginManager.java +++ b/briar-api/src/org/briarproject/api/plugins/PluginManager.java @@ -2,6 +2,7 @@ package org.briarproject.api.plugins; import java.util.Collection; +import org.briarproject.api.TransportId; import org.briarproject.api.lifecycle.Service; import org.briarproject.api.plugins.duplex.DuplexPlugin; @@ -11,6 +12,12 @@ import org.briarproject.api.plugins.duplex.DuplexPlugin; */ public interface PluginManager extends Service { + /** + * Returns the plugin for the given transport, or null if no such plugin + * is running. + */ + Plugin getPlugin(TransportId t); + /** Returns any running duplex plugins that support invitations. */ Collection<DuplexPlugin> getInvitationPlugins(); } diff --git a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java index b78aba1bb29c8f56633c954892651bb6c5ae8e2a..6dee08bb2c905de613e2599e0a730ff49269d686 100644 --- a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java +++ b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java @@ -9,6 +9,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; @@ -56,6 +57,7 @@ class PluginManagerImpl implements PluginManager { private final Poller poller; private final ConnectionDispatcher dispatcher; private final UiCallback uiCallback; + private final Map<TransportId, Plugin> plugins; private final List<SimplexPlugin> simplexPlugins; private final List<DuplexPlugin> duplexPlugins; @@ -73,6 +75,7 @@ class PluginManagerImpl implements PluginManager { this.poller = poller; this.dispatcher = dispatcher; this.uiCallback = uiCallback; + plugins = new ConcurrentHashMap<TransportId, Plugin>(); simplexPlugins = new CopyOnWriteArrayList<SimplexPlugin>(); duplexPlugins = new CopyOnWriteArrayList<DuplexPlugin>(); } @@ -104,10 +107,7 @@ class PluginManagerImpl implements PluginManager { } // Start the poller if(LOG.isLoggable(INFO)) LOG.info("Starting poller"); - List<Plugin> plugins = new ArrayList<Plugin>(); - plugins.addAll(simplexPlugins); - plugins.addAll(duplexPlugins); - poller.start(Collections.unmodifiableList(plugins)); + poller.start(plugins.values()); return true; } @@ -115,8 +115,7 @@ class PluginManagerImpl implements PluginManager { // Stop the poller if(LOG.isLoggable(INFO)) LOG.info("Stopping poller"); poller.stop(); - int plugins = simplexPlugins.size() + duplexPlugins.size(); - final CountDownLatch latch = new CountDownLatch(plugins); + final CountDownLatch latch = new CountDownLatch(plugins.size()); // Stop the simplex plugins if(LOG.isLoggable(INFO)) LOG.info("Stopping simplex plugins"); for(SimplexPlugin plugin : simplexPlugins) @@ -125,6 +124,7 @@ class PluginManagerImpl implements PluginManager { if(LOG.isLoggable(INFO)) LOG.info("Stopping duplex plugins"); for(DuplexPlugin plugin : duplexPlugins) pluginExecutor.execute(new PluginStopper(plugin, latch)); + plugins.clear(); simplexPlugins.clear(); duplexPlugins.clear(); // Wait for all the plugins to stop @@ -139,6 +139,10 @@ class PluginManagerImpl implements PluginManager { return true; } + public Plugin getPlugin(TransportId t) { + return plugins.get(t); + } + public Collection<DuplexPlugin> getInvitationPlugins() { List<DuplexPlugin> supported = new ArrayList<DuplexPlugin>(); for(DuplexPlugin d : duplexPlugins) @@ -185,6 +189,7 @@ class PluginManagerImpl implements PluginManager { boolean started = plugin.start(); long duration = clock.currentTimeMillis() - start; if(started) { + plugins.put(id, plugin); simplexPlugins.add(plugin); if(LOG.isLoggable(INFO)) { String name = plugin.getClass().getSimpleName(); @@ -246,6 +251,7 @@ class PluginManagerImpl implements PluginManager { boolean started = plugin.start(); long duration = clock.currentTimeMillis() - start; if(started) { + plugins.put(id, plugin); duplexPlugins.add(plugin); if(LOG.isLoggable(INFO)) { String name = plugin.getClass().getSimpleName(); diff --git a/briar-core/src/org/briarproject/plugins/file/FilePlugin.java b/briar-core/src/org/briarproject/plugins/file/FilePlugin.java index 6b286559aad9fcbec39a17f27f661419093aebec..77ed42e42c0d4b5d5a2a8b2eded20be8e2abb50b 100644 --- a/briar-core/src/org/briarproject/plugins/file/FilePlugin.java +++ b/briar-core/src/org/briarproject/plugins/file/FilePlugin.java @@ -55,6 +55,10 @@ public abstract class FilePlugin implements SimplexPlugin { return maxLatency; } + public boolean isRunning() { + return running; + } + public SimplexTransportReader createReader(ContactId c) { return null; } diff --git a/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java b/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java index 4f483dc89e808c69a72e416d8f519a4bc2844828..13401c290c7a3f873e41cc3ff618de7ec8a32521 100644 --- a/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java +++ b/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java @@ -146,6 +146,10 @@ abstract class TcpPlugin implements DuplexPlugin { if(socket != null) tryToClose(socket); } + public boolean isRunning() { + return running && socket != null && socket.isBound(); + } + public boolean shouldPoll() { return true; } diff --git a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java index 3bc65ce0f785aed83137c7457e6838caac57b037..4572df83e13c2a3213af9f8da414f6553bb6b3a6 100644 --- a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java +++ b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothPlugin.java @@ -172,6 +172,10 @@ class BluetoothPlugin implements DuplexPlugin { tryToClose(socket); } + public boolean isRunning() { + return running; + } + public boolean shouldPoll() { return true; } diff --git a/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java b/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java index 58f1c421e363c0d1f19a6856a330799aae100e13..4913cd82dbc0535b64be69e9f45d8a28874f2464 100644 --- a/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java +++ b/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java @@ -98,23 +98,8 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { } } - private boolean resetModem() { - if(!running) return false; - for(String portName : serialPortList.getPortNames()) { - if(LOG.isLoggable(INFO)) - LOG.info("Trying to initialise modem on " + portName); - modem = modemFactory.createModem(this, portName); - try { - if(!modem.start()) continue; - if(LOG.isLoggable(INFO)) - LOG.info("Initialised modem on " + portName); - return true; - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - running = false; - return false; + public boolean isRunning() { + return running; } public boolean shouldPoll() { @@ -180,6 +165,25 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { } } + private boolean resetModem() { + if(!running) return false; + for(String portName : serialPortList.getPortNames()) { + if(LOG.isLoggable(INFO)) + LOG.info("Trying to initialise modem on " + portName); + modem = modemFactory.createModem(this, portName); + try { + if(!modem.start()) continue; + if(LOG.isLoggable(INFO)) + LOG.info("Initialised modem on " + portName); + return true; + } catch(IOException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + } + } + running = false; + return false; + } + public DuplexTransportConnection createConnection(ContactId c) { if(!running) return null; // Get the ISO 3166 code for the caller's country