From 9dd3f81bb73f71ee0995b5c5699e5fe82e982118 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 22 Dec 2017 13:50:35 +0000 Subject: [PATCH 1/3] Use Tor's OR connection events to detect lost connectivity. --- .../bramble/plugin/tor/TorPlugin.java | 85 +++++++++++-------- 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index 1045f372c..e770bd286 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -60,6 +60,8 @@ import java.util.Scanner; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; import java.util.regex.Pattern; import java.util.zip.ZipInputStream; @@ -114,6 +116,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private final File torDirectory, torFile, geoIpFile, configFile; private final File doneFile, cookieFile; private final PowerManager.WakeLock wakeLock; + private final Lock connectionStatusLock; private final AtomicBoolean used = new AtomicBoolean(false); private volatile boolean running = false; @@ -152,6 +155,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { // This tag will prevent Huawei's powermanager from killing us. wakeLock = pm.newWakeLock(PARTIAL_WAKE_LOCK, "LocationManagerService"); wakeLock.setReferenceCounted(false); + connectionStatusLock = new ReentrantLock(); } @Override @@ -618,6 +622,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @Override public void orConnStatus(String status, String orName) { if (LOG.isLoggable(INFO)) LOG.info("OR connection " + status); + if (status.equals("CLOSED") || status.equals("FAILED")) + updateConnectionStatus(); // Check whether we've lost connectivity } @Override @@ -657,7 +663,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } @Override - public void onEvent(int event, String path) { + public void onEvent(int event, @Nullable String path) { stopWatching(); latch.countDown(); } @@ -677,45 +683,54 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private void updateConnectionStatus() { ioExecutor.execute(() -> { if (!running) return; - - Object o = appContext.getSystemService(CONNECTIVITY_SERVICE); - ConnectivityManager cm = (ConnectivityManager) o; - NetworkInfo net = cm.getActiveNetworkInfo(); - boolean online = net != null && net.isConnected(); - boolean wifi = online && net.getType() == TYPE_WIFI; - String country = locationUtils.getCurrentCountry(); - boolean blocked = TorNetworkMetadata.isTorProbablyBlocked( - country); - Settings s = callback.getSettings(); - int network = s.getInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_ALWAYS); - - if (LOG.isLoggable(INFO)) { - LOG.info("Online: " + online + ", wifi: " + wifi); - if ("".equals(country)) LOG.info("Country code unknown"); - else LOG.info("Country code: " + country); - } - try { - if (!online) { - LOG.info("Disabling network, device is offline"); - enableNetwork(false); - } else if (blocked) { - LOG.info("Disabling network, country is blocked"); - enableNetwork(false); - } else if (network == PREF_TOR_NETWORK_NEVER - || (network == PREF_TOR_NETWORK_WIFI && !wifi)) { - LOG.info("Disabling network due to data setting"); - enableNetwork(false); - } else { - LOG.info("Enabling network"); - enableNetwork(true); - } - } catch (IOException e) { - if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + connectionStatusLock.lock(); + updateConnectionStatusLocked(); + } finally { + connectionStatusLock.unlock(); } }); } + // Locking: connectionStatusLock + private void updateConnectionStatusLocked() { + Object o = appContext.getSystemService(CONNECTIVITY_SERVICE); + ConnectivityManager cm = (ConnectivityManager) o; + NetworkInfo net = cm.getActiveNetworkInfo(); + boolean online = net != null && net.isConnected(); + boolean wifi = online && net.getType() == TYPE_WIFI; + String country = locationUtils.getCurrentCountry(); + boolean blocked = TorNetworkMetadata.isTorProbablyBlocked( + country); + Settings s = callback.getSettings(); + int network = s.getInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_ALWAYS); + + if (LOG.isLoggable(INFO)) { + LOG.info("Online: " + online + ", wifi: " + wifi); + if ("".equals(country)) LOG.info("Country code unknown"); + else LOG.info("Country code: " + country); + } + + try { + if (!online) { + LOG.info("Disabling network, device is offline"); + enableNetwork(false); + } else if (blocked) { + LOG.info("Disabling network, country is blocked"); + enableNetwork(false); + } else if (network == PREF_TOR_NETWORK_NEVER + || (network == PREF_TOR_NETWORK_WIFI && !wifi)) { + LOG.info("Disabling network due to data setting"); + enableNetwork(false); + } else { + LOG.info("Enabling network"); + enableNetwork(true); + } + } catch (IOException e) { + if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + } + } + private class NetworkStateReceiver extends BroadcastReceiver { @Override From be2e68e96ccb84ff0c3eb9823435d5bd6abe1a41 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 22 Dec 2017 14:34:21 +0000 Subject: [PATCH 2/3] Listen for a wider range of connectivity-related events. --- .../bramble/plugin/tor/TorPlugin.java | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index e770bd286..ce6d5cdf0 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -11,6 +11,8 @@ import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.FileObserver; +import android.os.Handler; +import android.os.Looper; import android.os.PowerManager; import net.freehaven.tor.control.EventHandler; @@ -72,8 +74,12 @@ import javax.net.SocketFactory; import static android.content.Context.CONNECTIVITY_SERVICE; import static android.content.Context.MODE_PRIVATE; import static android.content.Context.POWER_SERVICE; +import static android.content.Intent.ACTION_SCREEN_OFF; +import static android.content.Intent.ACTION_SCREEN_ON; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.TYPE_WIFI; +import static android.os.Build.VERSION.SDK_INT; +import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED; import static android.os.PowerManager.PARTIAL_WAKE_LOCK; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.logging.Level.INFO; @@ -117,6 +123,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private final File doneFile, cookieFile; private final PowerManager.WakeLock wakeLock; private final Lock connectionStatusLock; + private final Handler handler; private final AtomicBoolean used = new AtomicBoolean(false); private volatile boolean running = false; @@ -156,6 +163,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { wakeLock = pm.newWakeLock(PARTIAL_WAKE_LOCK, "LocationManagerService"); wakeLock.setReferenceCounted(false); connectionStatusLock = new ReentrantLock(); + handler = new Handler(Looper.getMainLooper()); } @Override @@ -261,7 +269,11 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } // Register to receive network status events networkStateReceiver = new NetworkStateReceiver(); - IntentFilter filter = new IntentFilter(CONNECTIVITY_ACTION); + IntentFilter filter = new IntentFilter(); + filter.addAction(CONNECTIVITY_ACTION); + filter.addAction(ACTION_SCREEN_ON); + filter.addAction(ACTION_SCREEN_OFF); + if (SDK_INT >= 23) filter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED); appContext.registerReceiver(networkStateReceiver, filter); // Bind a server socket to receive incoming hidden service connections bind(); @@ -736,8 +748,17 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @Override public void onReceive(Context ctx, Intent i) { if (!running) return; - if (CONNECTIVITY_ACTION.equals(i.getAction())) { - LOG.info("Detected connectivity change"); + String action = i.getAction(); + if (LOG.isLoggable(INFO)) LOG.info("Received broadcast " + action); + if (CONNECTIVITY_ACTION.equals(action)) { + updateConnectionStatus(); + } else if (ACTION_SCREEN_ON.equals(action) + || ACTION_SCREEN_OFF.equals(action)) { + // Update connection status after 1 minute + handler.postDelayed(TorPlugin.this::updateConnectionStatus, + 60 * 1000); + } else if (SDK_INT >= 23 + && ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) { updateConnectionStatus(); } } From 29cd105a1d9bf0a4b6fd8c91b3f27cb6c3801e13 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 19 Jan 2018 12:21:47 +0000 Subject: [PATCH 3/3] Use scheduler service to schedule connectivity checks. --- .../bramble/plugin/AndroidPluginModule.java | 9 ++-- .../bramble/plugin/tor/TorPlugin.java | 48 +++++++++++-------- .../bramble/plugin/tor/TorPluginFactory.java | 12 +++-- 3 files changed, 41 insertions(+), 28 deletions(-) diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/AndroidPluginModule.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/AndroidPluginModule.java index 0dbc81b3e..d5f85f6f7 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/AndroidPluginModule.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/AndroidPluginModule.java @@ -13,6 +13,7 @@ import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.reporting.DevReporter; import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.LocationUtils; +import org.briarproject.bramble.api.system.Scheduler; import org.briarproject.bramble.plugin.droidtooth.DroidtoothPluginFactory; import org.briarproject.bramble.plugin.tcp.AndroidLanTcpPluginFactory; import org.briarproject.bramble.plugin.tor.TorPluginFactory; @@ -22,6 +23,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.concurrent.Executor; +import java.util.concurrent.ScheduledExecutorService; import javax.net.SocketFactory; @@ -33,6 +35,7 @@ public class AndroidPluginModule { @Provides PluginConfig providePluginConfig(@IoExecutor Executor ioExecutor, + @Scheduler ScheduledExecutorService scheduler, AndroidExecutor androidExecutor, SecureRandom random, SocketFactory torSocketFactory, BackoffFactory backoffFactory, Application app, LocationUtils locationUtils, DevReporter reporter, @@ -40,9 +43,9 @@ public class AndroidPluginModule { Context appContext = app.getApplicationContext(); DuplexPluginFactory bluetooth = new DroidtoothPluginFactory(ioExecutor, androidExecutor, appContext, random, eventBus, backoffFactory); - DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, appContext, - locationUtils, reporter, eventBus, torSocketFactory, - backoffFactory); + DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, scheduler, + appContext, locationUtils, reporter, eventBus, + torSocketFactory, backoffFactory); DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor, backoffFactory, appContext); Collection duplex = diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index ce6d5cdf0..67e48d3da 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -11,8 +11,6 @@ import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.FileObserver; -import android.os.Handler; -import android.os.Looper; import android.os.PowerManager; import net.freehaven.tor.control.EventHandler; @@ -61,7 +59,10 @@ import java.util.Map.Entry; import java.util.Scanner; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; @@ -82,6 +83,7 @@ import static android.os.Build.VERSION.SDK_INT; import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED; import static android.os.PowerManager.PARTIAL_WAKE_LOCK; import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS; @@ -110,6 +112,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { Logger.getLogger(TorPlugin.class.getName()); private final Executor ioExecutor; + private final ScheduledExecutorService scheduler; private final Context appContext; private final LocationUtils locationUtils; private final DevReporter reporter; @@ -123,7 +126,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private final File doneFile, cookieFile; private final PowerManager.WakeLock wakeLock; private final Lock connectionStatusLock; - private final Handler handler; + private final AtomicReference> connectivityCheck = + new AtomicReference<>(); private final AtomicBoolean used = new AtomicBoolean(false); private volatile boolean running = false; @@ -132,12 +136,13 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private volatile TorControlConnection controlConnection = null; private volatile BroadcastReceiver networkStateReceiver = null; - TorPlugin(Executor ioExecutor, Context appContext, - LocationUtils locationUtils, DevReporter reporter, - SocketFactory torSocketFactory, Backoff backoff, - DuplexPluginCallback callback, String architecture, int maxLatency, - int maxIdleTime) { + TorPlugin(Executor ioExecutor, ScheduledExecutorService scheduler, + Context appContext, LocationUtils locationUtils, + DevReporter reporter, SocketFactory torSocketFactory, + Backoff backoff, DuplexPluginCallback callback, + String architecture, int maxLatency, int maxIdleTime) { this.ioExecutor = ioExecutor; + this.scheduler = scheduler; this.appContext = appContext; this.locationUtils = locationUtils; this.reporter = reporter; @@ -163,7 +168,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { wakeLock = pm.newWakeLock(PARTIAL_WAKE_LOCK, "LocationManagerService"); wakeLock.setReferenceCounted(false); connectionStatusLock = new ReentrantLock(); - handler = new Handler(Looper.getMainLooper()); } @Override @@ -216,11 +220,11 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { if (LOG.isLoggable(INFO)) { Scanner stdout = new Scanner(torProcess.getInputStream()); Scanner stderr = new Scanner(torProcess.getErrorStream()); - while (stdout.hasNextLine() || stderr.hasNextLine()){ - if(stdout.hasNextLine()) { + while (stdout.hasNextLine() || stderr.hasNextLine()) { + if (stdout.hasNextLine()) { LOG.info(stdout.nextLine()); } - if(stderr.hasNextLine()){ + if (stderr.hasNextLine()) { LOG.info(stderr.nextLine()); } } @@ -743,6 +747,14 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } } + private void scheduleConnectionStatusUpdate() { + Future newConnectivityCheck = + scheduler.schedule(this::updateConnectionStatus, 1, MINUTES); + Future oldConnectivityCheck = + connectivityCheck.getAndSet(newConnectivityCheck); + if (oldConnectivityCheck != null) oldConnectivityCheck.cancel(false); + } + private class NetworkStateReceiver extends BroadcastReceiver { @Override @@ -750,16 +762,10 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { if (!running) return; String action = i.getAction(); if (LOG.isLoggable(INFO)) LOG.info("Received broadcast " + action); - if (CONNECTIVITY_ACTION.equals(action)) { - updateConnectionStatus(); - } else if (ACTION_SCREEN_ON.equals(action) + updateConnectionStatus(); + if (ACTION_SCREEN_ON.equals(action) || ACTION_SCREEN_OFF.equals(action)) { - // Update connection status after 1 minute - handler.postDelayed(TorPlugin.this::updateConnectionStatus, - 60 * 1000); - } else if (SDK_INT >= 23 - && ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) { - updateConnectionStatus(); + scheduleConnectionStatusUpdate(); } } } diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPluginFactory.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPluginFactory.java index 2f479d4ef..4ad1e8f3a 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPluginFactory.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPluginFactory.java @@ -17,6 +17,7 @@ import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.util.AndroidUtils; import java.util.concurrent.Executor; +import java.util.concurrent.ScheduledExecutorService; import java.util.logging.Logger; import javax.annotation.concurrent.Immutable; @@ -36,6 +37,7 @@ public class TorPluginFactory implements DuplexPluginFactory { private static final double BACKOFF_BASE = 1.2; private final Executor ioExecutor; + private final ScheduledExecutorService scheduler; private final Context appContext; private final LocationUtils locationUtils; private final DevReporter reporter; @@ -43,11 +45,13 @@ public class TorPluginFactory implements DuplexPluginFactory { private final SocketFactory torSocketFactory; private final BackoffFactory backoffFactory; - public TorPluginFactory(Executor ioExecutor, Context appContext, + public TorPluginFactory(Executor ioExecutor, + ScheduledExecutorService scheduler, Context appContext, LocationUtils locationUtils, DevReporter reporter, EventBus eventBus, SocketFactory torSocketFactory, BackoffFactory backoffFactory) { this.ioExecutor = ioExecutor; + this.scheduler = scheduler; this.appContext = appContext; this.locationUtils = locationUtils; this.reporter = reporter; @@ -89,9 +93,9 @@ public class TorPluginFactory implements DuplexPluginFactory { Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE); - TorPlugin plugin = new TorPlugin(ioExecutor, appContext, locationUtils, - reporter, torSocketFactory, backoff, callback, architecture, - MAX_LATENCY, MAX_IDLE_TIME); + TorPlugin plugin = new TorPlugin(ioExecutor, scheduler, appContext, + locationUtils, reporter, torSocketFactory, backoff, callback, + architecture, MAX_LATENCY, MAX_IDLE_TIME); eventBus.addListener(plugin); return plugin; }