diff --git a/bramble-android/src/main/java/org/briarproject/bramble/BrambleAndroidModule.java b/bramble-android/src/main/java/org/briarproject/bramble/BrambleAndroidModule.java index cb563508ae415e569d9fe81e5c225842b83f24ca..48c49794eb2bbe7b1458f2648806c3b6712554c9 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/BrambleAndroidModule.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/BrambleAndroidModule.java @@ -2,6 +2,7 @@ package org.briarproject.bramble; import android.app.Application; +import org.briarproject.bramble.network.AndroidNetworkModule; import org.briarproject.bramble.plugin.tor.CircumventionProvider; import org.briarproject.bramble.plugin.tor.CircumventionProviderImpl; import org.briarproject.bramble.system.AndroidSystemModule; @@ -12,6 +13,7 @@ import dagger.Module; import dagger.Provides; @Module(includes = { + AndroidNetworkModule.class, AndroidSystemModule.class }) public class BrambleAndroidModule { diff --git a/bramble-android/src/main/java/org/briarproject/bramble/network/AndroidNetworkManager.java b/bramble-android/src/main/java/org/briarproject/bramble/network/AndroidNetworkManager.java new file mode 100644 index 0000000000000000000000000000000000000000..490f8b4ec35213135bb9d8b4fc888d01461d9d6b --- /dev/null +++ b/bramble-android/src/main/java/org/briarproject/bramble/network/AndroidNetworkManager.java @@ -0,0 +1,142 @@ +package org.briarproject.bramble.network; + +import android.app.Application; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; + +import org.briarproject.bramble.api.event.EventBus; +import org.briarproject.bramble.api.lifecycle.Service; +import org.briarproject.bramble.api.network.NetworkManager; +import org.briarproject.bramble.api.network.NetworkStatus; +import org.briarproject.bramble.api.network.event.NetworkStatusEvent; +import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; +import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; +import org.briarproject.bramble.api.system.Scheduler; + +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.logging.Logger; + +import javax.annotation.Nullable; +import javax.inject.Inject; + +import static android.content.Context.CONNECTIVITY_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 java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.SECONDS; +import static java.util.logging.Level.INFO; + +@MethodsNotNullByDefault +@ParametersNotNullByDefault +class AndroidNetworkManager implements NetworkManager, Service { + + private static final Logger LOG = + Logger.getLogger(AndroidNetworkManager.class.getName()); + + // See android.net.wifi.WifiManager + private static final String WIFI_AP_STATE_CHANGED_ACTION = + "android.net.wifi.WIFI_AP_STATE_CHANGED"; + + private final ScheduledExecutorService scheduler; + private final EventBus eventBus; + private final Context appContext; + private final AtomicReference<Future<?>> connectivityCheck = + new AtomicReference<>(); + private final AtomicBoolean used = new AtomicBoolean(false); + + private volatile BroadcastReceiver networkStateReceiver = null; + + @Inject + AndroidNetworkManager(@Scheduler ScheduledExecutorService scheduler, + EventBus eventBus, Application app) { + this.scheduler = scheduler; + this.eventBus = eventBus; + this.appContext = app.getApplicationContext(); + } + + @Override + public void startService() { + if (used.getAndSet(true)) throw new IllegalStateException(); + // Register to receive network status events + networkStateReceiver = new NetworkStateReceiver(); + IntentFilter filter = new IntentFilter(); + filter.addAction(CONNECTIVITY_ACTION); + filter.addAction(ACTION_SCREEN_ON); + filter.addAction(ACTION_SCREEN_OFF); + filter.addAction(WIFI_AP_STATE_CHANGED_ACTION); + if (SDK_INT >= 23) filter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED); + appContext.registerReceiver(networkStateReceiver, filter); + + } + + @Override + public void stopService() { + if (networkStateReceiver != null) + appContext.unregisterReceiver(networkStateReceiver); + } + + @Override + public NetworkStatus getNetworkStatus() { + ConnectivityManager cm = (ConnectivityManager) + appContext.getSystemService(CONNECTIVITY_SERVICE); + assert cm != null; + NetworkInfo net = cm.getActiveNetworkInfo(); + boolean connected = net != null && net.isConnected(); + boolean wifi = connected && net.getType() == TYPE_WIFI; + return new NetworkStatus(connected, wifi); + } + + private void updateConnectionStatus() { + eventBus.broadcast(new NetworkStatusEvent(getNetworkStatus())); + } + + private void scheduleConnectionStatusUpdate(int delay, TimeUnit unit) { + Future<?> newConnectivityCheck = + scheduler.schedule(this::updateConnectionStatus, delay, unit); + Future<?> oldConnectivityCheck = + connectivityCheck.getAndSet(newConnectivityCheck); + if (oldConnectivityCheck != null) oldConnectivityCheck.cancel(false); + } + + private class NetworkStateReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context ctx, Intent i) { + String action = i.getAction(); + if (LOG.isLoggable(INFO)) LOG.info("Received broadcast " + action); + updateConnectionStatus(); + if (isSleepOrDozeEvent(action)) { + // Allow time for the network to be enabled or disabled + scheduleConnectionStatusUpdate(1, MINUTES); + } else if (isApEvent(action)) { + // The state change may be broadcast before the AP address is + // visible, so delay handling the event + scheduleConnectionStatusUpdate(5, SECONDS); + } + } + + private boolean isSleepOrDozeEvent(@Nullable String action) { + boolean isSleep = ACTION_SCREEN_ON.equals(action) || + ACTION_SCREEN_OFF.equals(action); + boolean isDoze = SDK_INT >= 23 && + ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action); + return isSleep || isDoze; + } + + private boolean isApEvent(@Nullable String action) { + return WIFI_AP_STATE_CHANGED_ACTION.equals(action); + } + } +} diff --git a/bramble-android/src/main/java/org/briarproject/bramble/network/AndroidNetworkModule.java b/bramble-android/src/main/java/org/briarproject/bramble/network/AndroidNetworkModule.java new file mode 100644 index 0000000000000000000000000000000000000000..b381b7a23a814a09f56e7646c03dece1df55ce3c --- /dev/null +++ b/bramble-android/src/main/java/org/briarproject/bramble/network/AndroidNetworkModule.java @@ -0,0 +1,21 @@ +package org.briarproject.bramble.network; + +import org.briarproject.bramble.api.lifecycle.LifecycleManager; +import org.briarproject.bramble.api.network.NetworkManager; + +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; + +@Module +public class AndroidNetworkModule { + + @Provides + @Singleton + NetworkManager provideNetworkManager(LifecycleManager lifecycleManager, + AndroidNetworkManager networkManager) { + lifecycleManager.registerService(networkManager); + return networkManager; + } +} diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPlugin.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPlugin.java index 506bc997f0dbc8b251cb12ecb038a14b51b39509..aa2591e8a497b175df7282be4dbbaa818602f4f4 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPlugin.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPlugin.java @@ -1,15 +1,16 @@ package org.briarproject.bramble.plugin.tcp; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkInfo; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; +import org.briarproject.bramble.PoliteExecutor; +import org.briarproject.bramble.api.event.Event; +import org.briarproject.bramble.api.event.EventListener; +import org.briarproject.bramble.api.network.event.NetworkStatusEvent; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback; @@ -20,7 +21,6 @@ import java.net.Socket; import java.net.UnknownHostException; import java.util.Collection; import java.util.concurrent.Executor; -import java.util.concurrent.ScheduledExecutorService; import java.util.logging.Logger; import javax.annotation.Nullable; @@ -28,21 +28,13 @@ import javax.net.SocketFactory; import static android.content.Context.CONNECTIVITY_SERVICE; import static android.content.Context.WIFI_SERVICE; -import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.TYPE_WIFI; -import static android.net.wifi.WifiManager.EXTRA_WIFI_STATE; import static android.os.Build.VERSION.SDK_INT; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; -import static java.util.concurrent.TimeUnit.SECONDS; @NotNullByDefault -class AndroidLanTcpPlugin extends LanTcpPlugin { - - // See android.net.wifi.WifiManager - private static final String WIFI_AP_STATE_CHANGED_ACTION = - "android.net.wifi.WIFI_AP_STATE_CHANGED"; - private static final int WIFI_AP_STATE_ENABLED = 13; +class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener { private static final byte[] WIFI_AP_ADDRESS_BYTES = {(byte) 192, (byte) 168, 43, 1}; @@ -60,25 +52,23 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { } } - private final ScheduledExecutorService scheduler; - private final Context appContext; + private final Executor connectionStatusExecutor; private final ConnectivityManager connectivityManager; @Nullable private final WifiManager wifiManager; - @Nullable - private volatile BroadcastReceiver networkStateReceiver = null; private volatile SocketFactory socketFactory; - AndroidLanTcpPlugin(Executor ioExecutor, ScheduledExecutorService scheduler, - Backoff backoff, Context appContext, DuplexPluginCallback callback, - int maxLatency, int maxIdleTime) { + AndroidLanTcpPlugin(Executor ioExecutor, Context appContext, + Backoff backoff, DuplexPluginCallback callback, int maxLatency, + int maxIdleTime) { super(ioExecutor, backoff, callback, maxLatency, maxIdleTime); - this.scheduler = scheduler; - this.appContext = appContext; + // Don't execute more than one connection status check at a time + connectionStatusExecutor = + new PoliteExecutor("AndroidLanTcpPlugin", ioExecutor, 1); ConnectivityManager connectivityManager = (ConnectivityManager) appContext.getSystemService(CONNECTIVITY_SERVICE); - if (connectivityManager == null) throw new AssertionError(); + assert connectivityManager != null; this.connectivityManager = connectivityManager; wifiManager = (WifiManager) appContext.getApplicationContext() .getSystemService(WIFI_SERVICE); @@ -89,19 +79,12 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { public void start() { if (used.getAndSet(true)) throw new IllegalStateException(); running = true; - // Register to receive network status events - networkStateReceiver = new NetworkStateReceiver(); - IntentFilter filter = new IntentFilter(); - filter.addAction(CONNECTIVITY_ACTION); - filter.addAction(WIFI_AP_STATE_CHANGED_ACTION); - appContext.registerReceiver(networkStateReceiver, filter); + updateConnectionStatus(); } @Override public void stop() { running = false; - if (networkStateReceiver != null) - appContext.unregisterReceiver(networkStateReceiver); tryToClose(socket); } @@ -120,7 +103,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { return singletonList(intToInetAddress(info.getIpAddress())); // If we're running an access point, return its address if (super.getLocalIpAddresses().contains(WIFI_AP_ADDRESS)) - return singletonList(WIFI_AP_ADDRESS); + return singletonList(WIFI_AP_ADDRESS); // No suitable addresses return emptyList(); } @@ -152,21 +135,13 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { return SocketFactory.getDefault(); } - private class NetworkStateReceiver extends BroadcastReceiver { - - @Override - public void onReceive(Context ctx, Intent i) { - if (!running) return; - if (isApEnabledEvent(i)) { - // The state change may be broadcast before the AP address is - // visible, so delay handling the event - scheduler.schedule(this::handleConnectivityChange, 1, SECONDS); - } else { - handleConnectivityChange(); - } - } + @Override + public void eventOccurred(Event e) { + if (e instanceof NetworkStatusEvent) updateConnectionStatus(); + } - private void handleConnectivityChange() { + private void updateConnectionStatus() { + connectionStatusExecutor.execute(() -> { if (!running) return; Collection<InetAddress> addrs = getLocalIpAddresses(); if (addrs.contains(WIFI_AP_ADDRESS)) { @@ -186,11 +161,6 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { socketFactory = getSocketFactory(); if (socket == null || socket.isClosed()) bind(); } - } - - private boolean isApEnabledEvent(Intent i) { - return WIFI_AP_STATE_CHANGED_ACTION.equals(i.getAction()) && - i.getIntExtra(EXTRA_WIFI_STATE, 0) == WIFI_AP_STATE_ENABLED; - } + }); } -} +} \ No newline at end of file diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPluginFactory.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPluginFactory.java index da8149c9a00ed1d29083734514040f9a43aa41e6..f8db864a0721402d781b723d5c65407abab88814 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPluginFactory.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPluginFactory.java @@ -2,6 +2,7 @@ package org.briarproject.bramble.plugin.tcp; import android.content.Context; +import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.BackoffFactory; @@ -11,7 +12,6 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import java.util.concurrent.Executor; -import java.util.concurrent.ScheduledExecutorService; import javax.annotation.concurrent.Immutable; @@ -28,15 +28,14 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory { private static final double BACKOFF_BASE = 1.2; private final Executor ioExecutor; - private final ScheduledExecutorService scheduler; + private final EventBus eventBus; private final BackoffFactory backoffFactory; private final Context appContext; - public AndroidLanTcpPluginFactory(Executor ioExecutor, - ScheduledExecutorService scheduler, BackoffFactory backoffFactory, - Context appContext) { + public AndroidLanTcpPluginFactory(Executor ioExecutor, EventBus eventBus, + BackoffFactory backoffFactory, Context appContext) { this.ioExecutor = ioExecutor; - this.scheduler = scheduler; + this.eventBus = eventBus; this.backoffFactory = backoffFactory; this.appContext = appContext; } @@ -55,7 +54,9 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory { public DuplexPlugin createPlugin(DuplexPluginCallback callback) { Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE); - return new AndroidLanTcpPlugin(ioExecutor, scheduler, backoff, - appContext, callback, MAX_LATENCY, MAX_IDLE_TIME); + AndroidLanTcpPlugin plugin = new AndroidLanTcpPlugin(ioExecutor, + appContext, backoff, callback, MAX_LATENCY, MAX_IDLE_TIME); + eventBus.addListener(plugin); + return plugin; } } 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 576ae75341614a894b57f1e50c71e153ecc3270c..c62ecb854e1352b19e9f2c60b72db88f62065cef 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 @@ -1,15 +1,10 @@ package org.briarproject.bramble.plugin.tor; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; import android.os.PowerManager; import net.freehaven.tor.control.EventHandler; @@ -21,6 +16,9 @@ import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.keyagreement.KeyAgreementListener; +import org.briarproject.bramble.api.network.NetworkManager; +import org.briarproject.bramble.api.network.NetworkStatus; +import org.briarproject.bramble.api.network.event.NetworkStatusEvent; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.plugin.Backoff; @@ -59,10 +57,8 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Scanner; 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.logging.Logger; import java.util.regex.Pattern; import java.util.zip.ZipInputStream; @@ -70,15 +66,8 @@ import java.util.zip.ZipInputStream; import javax.annotation.Nullable; 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.MINUTES; import static java.util.logging.Level.INFO; @@ -113,8 +102,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { Logger.getLogger(TorPlugin.class.getName()); private final Executor ioExecutor, connectionStatusExecutor; - private final ScheduledExecutorService scheduler; private final Context appContext; + private final NetworkManager networkManager; private final LocationUtils locationUtils; private final SocketFactory torSocketFactory; private final Clock clock; @@ -127,24 +116,23 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private final File torDirectory, torFile, geoIpFile, configFile; private final File doneFile, cookieFile; private final RenewableWakeLock wakeLock; - private final AtomicReference<Future<?>> connectivityCheck = - new AtomicReference<>(); private final AtomicBoolean used = new AtomicBoolean(false); private volatile boolean running = false; private volatile ServerSocket socket = null; private volatile Socket controlSocket = null; private volatile TorControlConnection controlConnection = null; - private volatile BroadcastReceiver networkStateReceiver = null; TorPlugin(Executor ioExecutor, ScheduledExecutorService scheduler, - Context appContext, LocationUtils locationUtils, - SocketFactory torSocketFactory, Clock clock, Backoff backoff, - DuplexPluginCallback callback, String architecture, - CircumventionProvider circumventionProvider, int maxLatency, int maxIdleTime) { + Context appContext, NetworkManager networkManager, + LocationUtils locationUtils, SocketFactory torSocketFactory, + Clock clock, CircumventionProvider circumventionProvider, + Backoff backoff, DuplexPluginCallback callback, + String architecture, + int maxLatency, int maxIdleTime) { this.ioExecutor = ioExecutor; - this.scheduler = scheduler; this.appContext = appContext; + this.networkManager = networkManager; this.locationUtils = locationUtils; this.torSocketFactory = torSocketFactory; this.clock = clock; @@ -165,8 +153,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { doneFile = new File(torDirectory, "done"); cookieFile = new File(torDirectory, ".tor/control_auth_cookie"); // Don't execute more than one connection status check at a time - connectionStatusExecutor = new PoliteExecutor("TorPlugin", - ioExecutor, 1); + connectionStatusExecutor = + new PoliteExecutor("TorPlugin", ioExecutor, 1); PowerManager pm = (PowerManager) appContext.getSystemService(POWER_SERVICE); wakeLock = new RenewableWakeLock(pm, scheduler, PARTIAL_WAKE_LOCK, @@ -271,14 +259,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } catch (IOException e) { throw new PluginException(e); } - // Register to receive network status events - networkStateReceiver = new NetworkStateReceiver(); - 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); + // Check whether we're online + updateConnectionStatus(networkManager.getNetworkStatus()); // Bind a server socket to receive incoming hidden service connections bind(); } @@ -517,8 +499,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { public void stop() { running = false; tryToClose(socket); - if (networkStateReceiver != null) - appContext.unregisterReceiver(networkStateReceiver); if (controlSocket != null && controlConnection != null) { try { LOG.info("Stopping Tor"); @@ -628,8 +608,10 @@ 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 + if (status.equals("CLOSED") || status.equals("FAILED")) { + // Check whether we've lost connectivity + updateConnectionStatus(networkManager.getNetworkStatus()); + } } @Override @@ -662,19 +644,18 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { SettingsUpdatedEvent s = (SettingsUpdatedEvent) e; if (s.getNamespace().equals(ID.getString())) { LOG.info("Tor settings updated"); - updateConnectionStatus(); + updateConnectionStatus(networkManager.getNetworkStatus()); } + } else if (e instanceof NetworkStatusEvent) { + updateConnectionStatus(((NetworkStatusEvent) e).getStatus()); } } - private void updateConnectionStatus() { + private void updateConnectionStatus(NetworkStatus status) { connectionStatusExecutor.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; + boolean online = status.isConnected(); + boolean wifi = status.isWifi(); String country = locationUtils.getCurrentCountry(); boolean blocked = circumventionProvider.isTorProbablyBlocked(country); @@ -715,29 +696,6 @@ 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 - public void onReceive(Context ctx, Intent i) { - if (!running) return; - String action = i.getAction(); - if (LOG.isLoggable(INFO)) LOG.info("Received broadcast " + action); - updateConnectionStatus(); - if (ACTION_SCREEN_ON.equals(action) - || ACTION_SCREEN_OFF.equals(action)) { - scheduleConnectionStatusUpdate(); - } - } - } - private static class ConnectionStatus { // All of the following are locking: this 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 a7feb23722f73aa7d77825fe4ecb3c8532c6e205..1055a03a7b9ab3e08fccc84704228b9409dfd1d0 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 @@ -4,6 +4,7 @@ import android.content.Context; import android.os.Build; import org.briarproject.bramble.api.event.EventBus; +import org.briarproject.bramble.api.network.NetworkManager; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.BackoffFactory; @@ -39,6 +40,7 @@ public class TorPluginFactory implements DuplexPluginFactory { private final Executor ioExecutor; private final ScheduledExecutorService scheduler; private final Context appContext; + private final NetworkManager networkManager; private final LocationUtils locationUtils; private final EventBus eventBus; private final SocketFactory torSocketFactory; @@ -48,13 +50,14 @@ public class TorPluginFactory implements DuplexPluginFactory { public TorPluginFactory(Executor ioExecutor, ScheduledExecutorService scheduler, Context appContext, - LocationUtils locationUtils, EventBus eventBus, - SocketFactory torSocketFactory, BackoffFactory backoffFactory, - CircumventionProvider circumventionProvider, - Clock clock) { + NetworkManager networkManager, LocationUtils locationUtils, + EventBus eventBus, SocketFactory torSocketFactory, + BackoffFactory backoffFactory, + CircumventionProvider circumventionProvider, Clock clock) { this.ioExecutor = ioExecutor; this.scheduler = scheduler; this.appContext = appContext; + this.networkManager = networkManager; this.locationUtils = locationUtils; this.eventBus = eventBus; this.torSocketFactory = torSocketFactory; @@ -97,8 +100,9 @@ public class TorPluginFactory implements DuplexPluginFactory { Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE); TorPlugin plugin = new TorPlugin(ioExecutor, scheduler, appContext, - locationUtils, torSocketFactory, clock, backoff, callback, - architecture, circumventionProvider, MAX_LATENCY, MAX_IDLE_TIME); + networkManager, locationUtils, torSocketFactory, clock, + circumventionProvider, backoff, callback, architecture, + MAX_LATENCY, MAX_IDLE_TIME); eventBus.addListener(plugin); return plugin; } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/network/NetworkManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/network/NetworkManager.java new file mode 100644 index 0000000000000000000000000000000000000000..f95eddc7d77df9fa71a042fda440b7f4d2307aa8 --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/network/NetworkManager.java @@ -0,0 +1,9 @@ +package org.briarproject.bramble.api.network; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +@NotNullByDefault +public interface NetworkManager { + + NetworkStatus getNetworkStatus(); +} diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/network/NetworkStatus.java b/bramble-api/src/main/java/org/briarproject/bramble/api/network/NetworkStatus.java new file mode 100644 index 0000000000000000000000000000000000000000..2cdb22e76899f24bb27cef0b1c530fdeec3481c1 --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/network/NetworkStatus.java @@ -0,0 +1,25 @@ +package org.briarproject.bramble.api.network; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +public class NetworkStatus { + + private final boolean connected, wifi; + + public NetworkStatus(boolean connected, boolean wifi) { + this.connected = connected; + this.wifi = wifi; + } + + public boolean isConnected() { + return connected; + } + + public boolean isWifi() { + return wifi; + } +} diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/network/event/NetworkStatusEvent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/network/event/NetworkStatusEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..f37bff94dc848584c70e032e47005d63da6ba9d5 --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/network/event/NetworkStatusEvent.java @@ -0,0 +1,22 @@ +package org.briarproject.bramble.api.network.event; + +import org.briarproject.bramble.api.event.Event; +import org.briarproject.bramble.api.network.NetworkStatus; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +public class NetworkStatusEvent extends Event { + + private final NetworkStatus status; + + public NetworkStatusEvent(NetworkStatus status) { + this.status = status; + } + + public NetworkStatus getStatus() { + return status; + } +} \ No newline at end of file diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java index 3af71627783a7632a3f2c848456f200207a7b318..06d2757670d6293aa2b595295e7fae859e24b3b2 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java @@ -11,6 +11,7 @@ import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.LifecycleManager; +import org.briarproject.bramble.api.network.NetworkManager; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.BackoffFactory; import org.briarproject.bramble.api.plugin.PluginConfig; @@ -92,17 +93,18 @@ public class AppModule { @Scheduler ScheduledExecutorService scheduler, AndroidExecutor androidExecutor, SecureRandom random, SocketFactory torSocketFactory, BackoffFactory backoffFactory, - Application app, LocationUtils locationUtils, EventBus eventBus, + Application app, NetworkManager networkManager, + LocationUtils locationUtils, EventBus eventBus, CircumventionProvider circumventionProvider, Clock clock) { Context appContext = app.getApplicationContext(); DuplexPluginFactory bluetooth = new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor, appContext, random, eventBus, backoffFactory); DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, scheduler, - appContext, locationUtils, eventBus, torSocketFactory, - backoffFactory, circumventionProvider, clock); + appContext, networkManager, locationUtils, eventBus, + torSocketFactory, backoffFactory, circumventionProvider, clock); DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor, - scheduler, backoffFactory, appContext); + eventBus, backoffFactory, appContext); Collection<DuplexPluginFactory> duplex = asList(bluetooth, tor, lan); @NotNullByDefault PluginConfig pluginConfig = new PluginConfig() {