diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml index 1df0b4897ad53800926576f5c861e6c86ecbc1f1..fd3030e50135d8ce9826633d35fef5f6edd7762d 100644 --- a/briar-android/res/values/strings.xml +++ b/briar-android/res/values/strings.xml @@ -102,6 +102,8 @@ <string name="bluetooth_setting">Connect via Bluetooth</string> <string name="bluetooth_setting_enabled">Whenever contacts are nearby</string> <string name="bluetooth_setting_disabled">Only when adding contacts</string> + <string name="tor_wifi_setting_title">TOR</string> + <string name="tor_wifi_setting"> Use Tor over WiFi Only</string> <string name="notification_settings_title">NOTIFICATIONS</string> <string name="notify_private_messages_setting">Show alerts for private messages</string> <string name="notify_group_posts_setting">Show alerts for forum posts</string> diff --git a/briar-android/src/org/briarproject/android/SettingsActivity.java b/briar-android/src/org/briarproject/android/SettingsActivity.java index 5bc03653c43064bc5739eb3fffe03171f1e211a3..fdbf99120c50f21cc54dd63af310cbf670c4b386 100644 --- a/briar-android/src/org/briarproject/android/SettingsActivity.java +++ b/briar-android/src/org/briarproject/android/SettingsActivity.java @@ -67,6 +67,7 @@ OnClickListener { private TextView enableBluetooth = null, enableBluetoothHint = null; private CheckBox notifyPrivateMessages = null, notifyGroupPosts = null; private CheckBox notifyVibration = null; + private CheckBox torOverWifi = null; private TextView notifySound = null, notifySoundHint = null; private ListLoadingProgressBar progress = null; private ImageButton testingButton = null; @@ -117,6 +118,23 @@ OnClickListener { enableBluetoothHint.setOnClickListener(this); settings.addView(enableBluetoothHint); + TextView torTitle = new TextView(this); + torTitle.setPadding(pad, 0, pad, 0); + torTitle.setTypeface(DEFAULT_BOLD); + torTitle.setTextColor(titleText); + torTitle.setText(R.string.tor_wifi_setting_title); + settings.addView(torTitle); + + underline = new HorizontalBorder(this); + underline.setBackgroundColor(titleUnderline); + settings.addView(underline); + + torOverWifi = new CheckBox(this); + torOverWifi.setTextSize(18); + torOverWifi.setText(R.string.tor_wifi_setting); + torOverWifi.setOnClickListener(this); + settings.addView(torOverWifi); + TextView notificationsTitle = new TextView(this); notificationsTitle.setPadding(pad, 0, pad, 0); notificationsTitle.setTypeface(DEFAULT_BOLD); @@ -280,6 +298,8 @@ OnClickListener { } storeBluetoothSetting(); displaySettings(); + } else if (view == torOverWifi) { + storeTorSettings(); } else if (view == notifyPrivateMessages) { Settings s = new Settings(); s.putBoolean("notifyPrivateMessages", @@ -312,6 +332,24 @@ OnClickListener { } } + private void storeTorSettings() { + runOnDbThread(new Runnable() { + public void run() { + Settings s = new Settings(); + s.putBoolean("torOverWifi", torOverWifi.isChecked()); + TransportConfig c = new TransportConfig(); + c.putBoolean("torOverWifi", torOverWifi.isChecked()); + storeSettings(s); + try { + db.mergeConfig(new TransportId("tor"), c); + } catch (DbException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + } + } + }); + } + private void storeBluetoothSetting() { runOnDbThread(new Runnable() { public void run() { diff --git a/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java b/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java index 12624c0dd397a0e0fa6e9b66c7f3ac2bab6b05d7..2016396234633cbc8ccd114411aed646023a0286 100644 --- a/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java +++ b/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java @@ -13,6 +13,7 @@ import org.briarproject.api.plugins.duplex.DuplexPluginFactory; import org.briarproject.api.plugins.simplex.SimplexPluginConfig; import org.briarproject.api.plugins.simplex.SimplexPluginFactory; import org.briarproject.api.system.LocationUtils; +import org.briarproject.api.event.EventBus; import org.briarproject.plugins.droidtooth.DroidtoothPluginFactory; import org.briarproject.plugins.tcp.AndroidLanTcpPluginFactory; import org.briarproject.plugins.tor.TorPluginFactory; @@ -36,12 +37,13 @@ public class AndroidPluginsModule extends PluginsModule { @Provides DuplexPluginConfig getDuplexPluginConfig(@IoExecutor Executor ioExecutor, AndroidExecutor androidExecutor, Application app, - CryptoComponent crypto, LocationUtils locationUtils) { + CryptoComponent crypto, LocationUtils locationUtils, + EventBus eventBus) { Context appContext = app.getApplicationContext(); DuplexPluginFactory bluetooth = new DroidtoothPluginFactory(ioExecutor, androidExecutor, appContext, crypto.getSecureRandom()); DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, appContext, - locationUtils); + locationUtils, eventBus); DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor, appContext); final Collection<DuplexPluginFactory> factories = diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java index 7cb0be2995ec1eced8f65f7bc3e71b659805085b..4d16f6eb6752893dcc05de4602becc5e2e27ec8e 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java @@ -50,11 +50,18 @@ import static android.content.Context.CONNECTIVITY_SERVICE; import static android.content.Context.MODE_PRIVATE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.EXTRA_NO_CONNECTIVITY; +import static android.net.ConnectivityManager.TYPE_WIFI; + +import org.briarproject.api.event.EventListener; +import org.briarproject.api.event.Event; +import org.briarproject.api.event.SettingsUpdatedEvent; + import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; -class TorPlugin implements DuplexPlugin, EventHandler { +class TorPlugin implements DuplexPlugin, EventHandler, + EventListener { static final TransportId ID = new TransportId("tor"); @@ -82,6 +89,9 @@ class TorPlugin implements DuplexPlugin, EventHandler { private volatile boolean running = false, networkEnabled = false; private volatile boolean bootstrapped = false; + private volatile boolean connectedToWifi = false; + private volatile boolean online = false; + private volatile ServerSocket socket = null; private volatile Socket controlSocket = null; private volatile TorControlConnection controlConnection = null; @@ -585,27 +595,94 @@ class TorPlugin implements DuplexPlugin, EventHandler { @Override public void onReceive(Context ctx, Intent i) { + if (!running) return; - boolean online = !i.getBooleanExtra(EXTRA_NO_CONNECTIVITY, false); + + Object o = ctx.getSystemService(CONNECTIVITY_SERVICE); + ConnectivityManager cm = (ConnectivityManager) o; + NetworkInfo net = cm.getActiveNetworkInfo(); + + /* Some devices fail to set EXTRA_NO_CONNECTIVITY, double check */ + online = !i.getBooleanExtra(EXTRA_NO_CONNECTIVITY, false); if (online) { - // Some devices fail to set EXTRA_NO_CONNECTIVITY, double check - Object o = ctx.getSystemService(CONNECTIVITY_SERVICE); - ConnectivityManager cm = (ConnectivityManager) o; - NetworkInfo net = cm.getActiveNetworkInfo(); if (net == null || !net.isConnected()) online = false; } - String country = locationUtils.getCurrentCountry(); - if (LOG.isLoggable(INFO)) { - LOG.info("Online: " + online); - if ("".equals(country)) LOG.info("Country code unknown"); - else LOG.info("Country code: " + country); - } - boolean blocked = TorNetworkMetadata.isTorProbablyBlocked(country); - try { - enableNetwork(online && !blocked); - } catch (IOException e) { - if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } + + connectedToWifi = (net != null && net.getType() == TYPE_WIFI + && net.isConnected()); + + updateConnectionStatus(); } } + + public void eventOccurred(Event e) { + if (e instanceof SettingsUpdatedEvent) { + if (!running) return; + + updateConnectionStatus(); + } + } + + private void updateConnectionStatus() { + + ioExecutor.execute(new Runnable() { + + public void run() { + + boolean wifiOnly = false; + boolean blocked = false; + + String country = locationUtils.getCurrentCountry(); + if (LOG.isLoggable(INFO)) { + LOG.info("Online: " + online); + if ("".equals(country)) LOG.info("Country code unknown"); + else LOG.info("Country code: " + country); + } + blocked = TorNetworkMetadata.isTorProbablyBlocked(country); + TransportConfig c = callback.getConfig(); + wifiOnly = c.getBoolean("torOverWifi", false); + + try { + /* + 1) Disable network if offline + */ + if (!online) { + LOG.log(WARNING, "Disabling network, network is offline"); + enableNetwork(false); + return; + } + + /* + 2) Disable network if blocked + */ + if (blocked) { + LOG.log(WARNING, "Disabling network, country is blocked"); + enableNetwork(false); + return; + } + + /* + 3) Disable network if wifiOnly and not connected to + wifi + */ + if (wifiOnly & !connectedToWifi){ + LOG.log(WARNING, "Disabling network due to wifi only setting"); + enableNetwork(false); + return; + } + + /* + 4) Otherwise enable network + */ + enableNetwork(true); + + } catch (IOException e) { + if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + } + + } + + }); + } + } diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java b/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java index 9e38a5a376f41f9de371460513330f576489ebfb..d9472ad44841dc5adf0409c5cbe2dc4c543b7cf7 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java @@ -3,6 +3,8 @@ package org.briarproject.plugins.tor; import java.util.concurrent.Executor; import java.util.logging.Logger; +import org.briarproject.api.event.EventBus; + import org.briarproject.android.util.AndroidUtils; import org.briarproject.api.TransportId; import org.briarproject.api.plugins.duplex.DuplexPlugin; @@ -25,12 +27,14 @@ public class TorPluginFactory implements DuplexPluginFactory { private final Executor ioExecutor; private final Context appContext; private final LocationUtils locationUtils; + private final EventBus eventBus; public TorPluginFactory(Executor ioExecutor, Context appContext, - LocationUtils locationUtils) { + LocationUtils locationUtils, EventBus eventBus) { this.ioExecutor = ioExecutor; this.appContext = appContext; this.locationUtils = locationUtils; + this.eventBus = eventBus; } public TransportId getId() { @@ -38,6 +42,9 @@ public class TorPluginFactory implements DuplexPluginFactory { } public DuplexPlugin createPlugin(DuplexPluginCallback callback) { + + TorPlugin thisPlugin = null; + // Check that we have a Tor binary for this architecture String architecture = null; for (String abi : AndroidUtils.getSupportedArchitectures()) { @@ -55,7 +62,13 @@ public class TorPluginFactory implements DuplexPluginFactory { } // Use position-independent executable for SDK >= 16 if (Build.VERSION.SDK_INT >= 16) architecture += "-pie"; - return new TorPlugin(ioExecutor,appContext, locationUtils, callback, - architecture, MAX_LATENCY, MAX_IDLE_TIME, POLLING_INTERVAL); + + thisPlugin = new TorPlugin(ioExecutor,appContext, locationUtils, + callback, architecture, MAX_LATENCY, MAX_IDLE_TIME, + POLLING_INTERVAL); + this.eventBus.addListener(thisPlugin); + + return thisPlugin; + } }