From fcc26c093b5e53a0dbb7ac047857185f4f578d4e Mon Sep 17 00:00:00 2001 From: akwizgran Date: Tue, 14 Jan 2020 09:51:03 +0000 Subject: [PATCH 01/69] Avoid NPE if there's no TelephonyManager. --- .../org/briarproject/bramble/system/AndroidLocationUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bramble-android/src/main/java/org/briarproject/bramble/system/AndroidLocationUtils.java b/bramble-android/src/main/java/org/briarproject/bramble/system/AndroidLocationUtils.java index 226064d6f..21d2075c1 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/system/AndroidLocationUtils.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/system/AndroidLocationUtils.java @@ -61,12 +61,12 @@ class AndroidLocationUtils implements LocationUtils { private String getCountryFromPhoneNetwork() { Object o = appContext.getSystemService(TELEPHONY_SERVICE); TelephonyManager tm = (TelephonyManager) o; - return tm.getNetworkCountryIso(); + return tm == null ? "" : tm.getNetworkCountryIso(); } private String getCountryFromSimCard() { Object o = appContext.getSystemService(TELEPHONY_SERVICE); TelephonyManager tm = (TelephonyManager) o; - return tm.getSimCountryIso(); + return tm == null ? "" : tm.getSimCountryIso(); } } From b2a1ea84f8b6e7388f791febc362183ba9eb2b02 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Tue, 14 Jan 2020 12:18:24 +0000 Subject: [PATCH 02/69] Provide more information about plugin states. --- .../plugin/tcp/AndroidLanTcpPlugin.java | 20 ++- .../bramble/plugin/tor/AndroidTorPlugin.java | 2 +- .../bramble/api/plugin/Plugin.java | 32 ++++- .../bramble/api/plugin/PluginCallback.java | 10 +- .../bramble/plugin/PluginManagerImpl.java | 34 +++-- .../plugin/bluetooth/BluetoothPlugin.java | 107 +++++++++++---- .../bramble/plugin/file/FilePlugin.java | 5 +- .../bramble/plugin/tcp/LanTcpPlugin.java | 12 +- .../bramble/plugin/tcp/TcpPlugin.java | 108 ++++++++++----- .../bramble/plugin/tor/TorPlugin.java | 126 ++++++++++++------ .../bramble/plugin/tcp/LanTcpPluginTest.java | 7 +- .../bramble/plugin/modem/ModemPlugin.java | 71 ++++++++-- .../navdrawer/NavDrawerControllerImpl.java | 3 +- 13 files changed, 386 insertions(+), 151 deletions(-) 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 d243668ee..14e18cb51 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 @@ -33,6 +33,8 @@ import static android.os.Build.VERSION.SDK_INT; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static java.util.logging.Logger.getLogger; +import static org.briarproject.bramble.api.plugin.Plugin.State.AVAILABLE; +import static org.briarproject.bramble.api.plugin.Plugin.State.UNAVAILABLE; @NotNullByDefault class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener { @@ -68,16 +70,11 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener { public void start() { if (used.getAndSet(true)) throw new IllegalStateException(); initialisePortProperty(); - running = true; + state.setStarted(); + callback.pluginStateChanged(getState()); updateConnectionStatus(); } - @Override - public void stop() { - running = false; - tryToClose(socket); - } - @Override protected Socket createSocket() throws IOException { return socketFactory.createSocket(); @@ -135,7 +132,8 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener { private void updateConnectionStatus() { connectionStatusExecutor.execute(() -> { - if (!running) return; + State state = getState(); + if (state != AVAILABLE && state != UNAVAILABLE) return; List addrs = getUsableLocalInetAddresses(); if (addrs.contains(WIFI_AP_ADDRESS) || addrs.contains(WIFI_DIRECT_AP_ADDRESS)) { @@ -145,15 +143,15 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener { // make outgoing connections on API 21+ if another network // has internet access socketFactory = SocketFactory.getDefault(); - if (socket == null || socket.isClosed()) bind(); + if (state == UNAVAILABLE) bind(); } else if (addrs.isEmpty()) { LOG.info("Not connected to wifi"); socketFactory = SocketFactory.getDefault(); - tryToClose(socket); + // TODO: Check that socket was closed when interface went down } else { LOG.info("Connected to wifi"); socketFactory = getSocketFactory(); - if (socket == null || socket.isClosed()) bind(); + if (state == UNAVAILABLE) bind(); } }); } diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPlugin.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPlugin.java index bf1352479..715c80a83 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPlugin.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPlugin.java @@ -75,7 +75,7 @@ class AndroidTorPlugin extends TorPlugin { @Override protected void enableNetwork(boolean enable) throws IOException { - if (!running) return; + if (!state.isRunning()) return; if (enable) wakeLock.acquire(); super.enableNetwork(enable); if (!enable) wakeLock.release(); diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java index 84872dfed..b26a37159 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java @@ -9,6 +9,34 @@ import java.util.Collection; @NotNullByDefault public interface Plugin { + enum State { + + /** + * The plugin has not been started, has been stopped, or is disabled by + * settings. + */ + DISABLED, + + /** + * The plugin has been started, has not been stopped, is enabled by + * settings, but can't yet tell whether it can make or receive + * connections. + */ + ENABLING, + + /** + * The plugin has been started, has not been stopped, is enabled by + * settings, and can make or receive connections. + */ + AVAILABLE, + + /** + * The plugin has been started, has not been stopped, is enabled by + * settings, but can't make or receive connections + */ + UNAVAILABLE + } + /** * Returns the plugin's transport identifier. */ @@ -35,9 +63,9 @@ public interface Plugin { void stop() throws PluginException; /** - * Returns true if the plugin is running. + * Returns the current state of the plugin. */ - boolean isRunning(); + State getState(); /** * Returns true if the plugin should be polled periodically to attempt to diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/PluginCallback.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/PluginCallback.java index b9cbd450b..ed057a958 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/PluginCallback.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/PluginCallback.java @@ -1,6 +1,7 @@ package org.briarproject.bramble.api.plugin; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.Plugin.State; import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.settings.Settings; @@ -32,12 +33,7 @@ public interface PluginCallback extends ConnectionHandler { void mergeLocalProperties(TransportProperties p); /** - * Signals that the transport is enabled. + * Signals that the transport's state may have changed. */ - void transportEnabled(); - - /** - * Signals that the transport is disabled. - */ - void transportDisabled(); + void pluginStateChanged(State state); } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java index c7b33692a..c3ded073b 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java @@ -8,6 +8,7 @@ import org.briarproject.bramble.api.lifecycle.Service; import org.briarproject.bramble.api.lifecycle.ServiceException; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.Plugin; +import org.briarproject.bramble.api.plugin.Plugin.State; import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginConfig; import org.briarproject.bramble.api.plugin.PluginException; @@ -36,6 +37,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Logger; import javax.annotation.concurrent.ThreadSafe; @@ -45,6 +47,8 @@ import static java.util.logging.Level.FINE; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; +import static org.briarproject.bramble.api.plugin.Plugin.State.AVAILABLE; +import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.now; @@ -250,6 +254,8 @@ class PluginManagerImpl implements PluginManager, Service { private class Callback implements PluginCallback { private final TransportId id; + private final AtomicReference state = + new AtomicReference<>(DISABLED); private final AtomicBoolean enabled = new AtomicBoolean(false); private Callback(TransportId id) { @@ -295,15 +301,25 @@ class PluginManagerImpl implements PluginManager, Service { } @Override - public void transportEnabled() { - if (!enabled.getAndSet(true)) - eventBus.broadcast(new TransportEnabledEvent(id)); - } - - @Override - public void transportDisabled() { - if (enabled.getAndSet(false)) - eventBus.broadcast(new TransportDisabledEvent(id)); + public void pluginStateChanged(State newState) { + State oldState = state.getAndSet(newState); + if (newState != oldState) { + if (LOG.isLoggable(INFO)) { + LOG.info(id + " changed from state " + oldState + + " to " + newState); + } + if (newState == AVAILABLE) { + if (!enabled.getAndSet(true)) + eventBus.broadcast(new TransportEnabledEvent(id)); + } else { + if (enabled.getAndSet(false)) + eventBus.broadcast(new TransportDisabledEvent(id)); + } + } else { + // TODO: Remove + if (LOG.isLoggable(INFO)) + LOG.info(id + " stayed in state " + oldState); + } } @Override diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java index d8315df38..2747ca008 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java @@ -11,6 +11,7 @@ import org.briarproject.bramble.api.keyagreement.KeyAgreementListener; import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent; import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStoppedListeningEvent; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.ConnectionHandler; @@ -37,6 +38,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; import javax.annotation.Nullable; +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.ThreadSafe; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; @@ -47,6 +50,9 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENA import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID; import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES; +import static org.briarproject.bramble.api.plugin.Plugin.State.AVAILABLE; +import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; +import static org.briarproject.bramble.api.plugin.Plugin.State.UNAVAILABLE; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; @@ -70,9 +76,10 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { private final int maxLatency, maxIdleTime; private final AtomicBoolean used = new AtomicBoolean(false); - private volatile boolean running = false, contactConnections = false; + protected final PluginState state = new PluginState(); + + private volatile boolean contactConnections = false; private volatile String contactConnectionsUuid = null; - private volatile SS socket = null; abstract void initialiseAdapter() throws IOException; @@ -125,13 +132,16 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { // We may not have been able to get the local address before ioExecutor.execute(this::updateProperties); if (shouldAllowContactConnections()) bind(); + callback.pluginStateChanged(getState()); } void onAdapterDisabled() { LOG.info("Bluetooth disabled"); - tryToClose(socket); + // TODO: Is this needed, or will the socket be closed automatically? + SS ss = state.clearServerSocket(); + tryToClose(ss); connectionLimiter.allConnectionsClosed(); - callback.transportDisabled(); + callback.pluginStateChanged(getState()); } @Override @@ -158,7 +168,8 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { throw new PluginException(e); } updateProperties(); - running = true; + state.setStarted(); + callback.pluginStateChanged(getState()); loadSettings(callback.getSettings()); if (shouldAllowContactConnections()) { if (isAdapterEnabled()) bind(); @@ -176,7 +187,8 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { private void bind() { ioExecutor.execute(() -> { - if (!isRunning() || !shouldAllowContactConnections()) return; + if (!shouldAllowContactConnections() || getState() != AVAILABLE) + return; // Bind a server socket to accept connections from contacts SS ss; try { @@ -185,14 +197,15 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { logException(LOG, WARNING, e); return; } - if (!isRunning() || !shouldAllowContactConnections()) { + if (!shouldAllowContactConnections() || + !state.setServerSocket(ss)) { + LOG.info("Closing redundant server socket"); tryToClose(ss); return; } - socket = ss; backoff.reset(); - callback.transportEnabled(); - acceptContactConnections(); + callback.pluginStateChanged(getState()); + acceptContactConnections(ss); }); } @@ -221,35 +234,36 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { if (changed) callback.mergeLocalProperties(p); } - private void acceptContactConnections() { + private void acceptContactConnections(SS ss) { while (true) { DuplexTransportConnection conn; try { - conn = acceptConnection(socket); + conn = acceptConnection(ss); } catch (IOException e) { // This is expected when the socket is closed - if (LOG.isLoggable(INFO)) LOG.info(e.toString()); + // TODO: Check that this is logged at shutdown/when BT disabled + LOG.info("Server socket closed"); + state.clearServerSocket(); return; } LOG.info("Connection received"); connectionLimiter.connectionOpened(conn); backoff.reset(); callback.handleConnection(conn); - if (!running) return; } } @Override public void stop() { - running = false; - tryToClose(socket); - callback.transportDisabled(); + SS ss = state.setStopped(); + callback.pluginStateChanged(getState()); + tryToClose(ss); disableAdapterIfEnabledByUs(); } @Override - public boolean isRunning() { - return running && isAdapterEnabled(); + public State getState() { + return state.getState(); } @Override @@ -265,7 +279,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { @Override public void poll(Collection> properties) { - if (!isRunning() || !shouldAllowContactConnections()) return; + if (!shouldAllowContactConnections() || getState() != AVAILABLE) return; backoff.increment(); for (Pair p : properties) { connect(p.getFirst(), p.getSecond()); @@ -319,7 +333,8 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { @Override public DuplexTransportConnection createConnection(TransportProperties p) { - if (!isRunning() || !shouldAllowContactConnections()) return null; + if (!shouldAllowContactConnections() || getState() != AVAILABLE) + return null; if (!connectionLimiter.canOpenContactConnection()) return null; String address = p.get(PROP_ADDRESS); if (isNullOrEmpty(address)) return null; @@ -337,7 +352,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { @Override public KeyAgreementListener createKeyAgreementListener(byte[] commitment) { - if (!isRunning()) return null; + if (getState() != AVAILABLE) return null; // No truncation necessary because COMMIT_LENGTH = 16 String uuid = UUID.nameUUIDFromBytes(commitment).toString(); if (LOG.isLoggable(INFO)) LOG.info("Key agreement UUID " + uuid); @@ -349,7 +364,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { logException(LOG, WARNING, e); return null; } - if (!isRunning()) { + if (getState() != AVAILABLE) { tryToClose(ss); return null; } @@ -363,7 +378,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { @Override public DuplexTransportConnection createKeyAgreementConnection( byte[] commitment, BdfList descriptor) { - if (!isRunning()) return null; + if (getState() != AVAILABLE) return null; // No truncation necessary because COMMIT_LENGTH = 16 String uuid = UUID.nameUUIDFromBytes(commitment).toString(); DuplexTransportConnection conn; @@ -429,8 +444,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { boolean isAllowed = shouldAllowContactConnections(); if (wasAllowed && !isAllowed) { LOG.info("Contact connections disabled"); - tryToClose(socket); - callback.transportDisabled(); + tryToClose(state.clearServerSocket()); disableAdapterIfEnabledByUs(); } else if (!wasAllowed && isAllowed) { LOG.info("Contact connections enabled"); @@ -461,4 +475,45 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { tryToClose(ss); } } + + @ThreadSafe + @NotNullByDefault + protected class PluginState { + + @GuardedBy("this") + private boolean started = false, stopped = false; + @GuardedBy("this") + @Nullable + private SS serverSocket = null; + + synchronized void setStarted() { + started = true; + } + + @Nullable + synchronized SS setStopped() { + stopped = true; + SS ss = serverSocket; + serverSocket = null; + return ss; + } + + synchronized boolean setServerSocket(SS ss) { + if (stopped || serverSocket != null) return false; + serverSocket = ss; + return true; + } + + @Nullable + synchronized SS clearServerSocket() { + SS ss = serverSocket; + serverSocket = null; + return ss; + } + + synchronized State getState() { + if (!started || stopped) return DISABLED; + return isAdapterEnabled() ? AVAILABLE : UNAVAILABLE; + } + } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/file/FilePlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/file/FilePlugin.java index 0d4f56618..d846fd2ae 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/file/FilePlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/file/FilePlugin.java @@ -16,6 +16,7 @@ import java.util.logging.Logger; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.plugin.FileConstants.PROP_PATH; +import static org.briarproject.bramble.api.plugin.Plugin.State.AVAILABLE; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; @@ -45,7 +46,7 @@ abstract class FilePlugin implements SimplexPlugin { @Override public TransportConnectionReader createReader(TransportProperties p) { - if (!isRunning()) return null; + if (getState() != AVAILABLE) return null; String path = p.get(PROP_PATH); if (isNullOrEmpty(path)) return null; try { @@ -60,7 +61,7 @@ abstract class FilePlugin implements SimplexPlugin { @Override public TransportConnectionWriter createWriter(TransportProperties p) { - if (!isRunning()) return null; + if (getState() != AVAILABLE) return null; String path = p.get(PROP_PATH); if (isNullOrEmpty(path)) return null; try { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java index e955e871c..bd980cff6 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java @@ -11,7 +11,6 @@ import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.settings.Settings; -import org.briarproject.bramble.util.IoUtils; import java.io.IOException; import java.net.Inet4Address; @@ -41,6 +40,7 @@ import static org.briarproject.bramble.api.plugin.LanTcpConstants.PREF_LAN_IP_PO import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_IP_PORTS; import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_PORT; import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED; +import static org.briarproject.bramble.util.IoUtils.tryToClose; import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; import static org.briarproject.bramble.util.StringUtils.join; @@ -271,10 +271,10 @@ class LanTcpPlugin extends TcpPlugin { } catch (IOException e) { if (LOG.isLoggable(INFO)) LOG.info("Failed to bind " + scrubSocketAddress(addr)); - tryToClose(ss); + tryToClose(ss, LOG, WARNING); } } - if (ss == null || !ss.isBound()) { + if (ss == null) { LOG.info("Could not bind server socket for key agreement"); return null; } @@ -290,8 +290,8 @@ class LanTcpPlugin extends TcpPlugin { @Override public DuplexTransportConnection createKeyAgreementConnection( byte[] commitment, BdfList descriptor) { - if (!isRunning()) return null; - ServerSocket ss = socket; + ServerSocket ss = state.getServerSocket(); + if (ss == null) return null; InterfaceAddress local = getLocalInterfaceAddress(ss.getInetAddress()); if (local == null) { LOG.warning("No interface for key agreement server socket"); @@ -363,7 +363,7 @@ class LanTcpPlugin extends TcpPlugin { @Override public void close() { - IoUtils.tryToClose(ss, LOG, WARNING); + tryToClose(ss, LOG, WARNING); } } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java index 4f11054a0..07b4bf782 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java @@ -5,6 +5,7 @@ import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.keyagreement.KeyAgreementListener; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.ConnectionHandler; @@ -14,7 +15,6 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.rendezvous.KeyMaterialSource; import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint; -import org.briarproject.bramble.util.IoUtils; import java.io.IOException; import java.net.InetAddress; @@ -35,12 +35,18 @@ import java.util.logging.Logger; import java.util.regex.Pattern; import javax.annotation.Nullable; +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.ThreadSafe; import static java.util.Collections.emptyList; import static java.util.Collections.list; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; +import static org.briarproject.bramble.api.plugin.Plugin.State.AVAILABLE; +import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; +import static org.briarproject.bramble.api.plugin.Plugin.State.UNAVAILABLE; +import static org.briarproject.bramble.util.IoUtils.tryToClose; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; @@ -60,9 +66,7 @@ abstract class TcpPlugin implements DuplexPlugin { protected final int maxLatency, maxIdleTime; protected final int connectionTimeout, socketTimeout; protected final AtomicBoolean used = new AtomicBoolean(false); - - protected volatile boolean running = false; - protected volatile ServerSocket socket = null; + protected final PluginState state = new PluginState(); /** * Returns zero or more socket addresses on which the plugin should listen, @@ -118,14 +122,14 @@ abstract class TcpPlugin implements DuplexPlugin { @Override public void start() { if (used.getAndSet(true)) throw new IllegalStateException(); - running = true; + state.setStarted(); + callback.pluginStateChanged(getState()); bind(); } protected void bind() { bindExecutor.execute(() -> { - if (!running) return; - if (socket != null && !socket.isClosed()) return; + if (getState() != UNAVAILABLE) return; ServerSocket ss = null; for (InetSocketAddress addr : getLocalSocketAddresses()) { try { @@ -135,34 +139,29 @@ abstract class TcpPlugin implements DuplexPlugin { } catch (IOException e) { if (LOG.isLoggable(INFO)) LOG.info("Failed to bind " + scrubSocketAddress(addr)); - tryToClose(ss); + tryToClose(ss, LOG, WARNING); } } - if (ss == null || !ss.isBound()) { + if (ss == null) { LOG.info("Could not bind server socket"); return; } - if (!running) { - tryToClose(ss); + if (!state.setServerSocket(ss)) { + LOG.info("Closing redundant server socket"); + tryToClose(ss, LOG, WARNING); return; } - socket = ss; backoff.reset(); InetSocketAddress local = (InetSocketAddress) ss.getLocalSocketAddress(); setLocalSocketAddress(local); + callback.pluginStateChanged(getState()); if (LOG.isLoggable(INFO)) LOG.info("Listening on " + scrubSocketAddress(local)); - callback.transportEnabled(); - acceptContactConnections(); + acceptContactConnections(ss); }); } - protected void tryToClose(@Nullable ServerSocket ss) { - IoUtils.tryToClose(ss, LOG, WARNING); - callback.transportDisabled(); - } - String getIpPortString(InetSocketAddress a) { String addr = a.getAddress().getHostAddress(); int percent = addr.indexOf('%'); @@ -170,15 +169,18 @@ abstract class TcpPlugin implements DuplexPlugin { return addr + ":" + a.getPort(); } - private void acceptContactConnections() { - while (isRunning()) { + private void acceptContactConnections(ServerSocket ss) { + while (true) { Socket s; try { - s = socket.accept(); + s = ss.accept(); s.setSoTimeout(socketTimeout); } catch (IOException e) { - // This is expected when the socket is closed - if (LOG.isLoggable(INFO)) LOG.info(e.toString()); + // This is expected when the server socket is closed + // TODO: Check that this is logged at shutdown/when LAN disabled + LOG.info("Server socket closed"); + state.clearServerSocket(ss); + callback.pluginStateChanged(getState()); return; } if (LOG.isLoggable(INFO)) @@ -191,13 +193,14 @@ abstract class TcpPlugin implements DuplexPlugin { @Override public void stop() { - running = false; - tryToClose(socket); + ServerSocket ss = state.setStopped(); + callback.pluginStateChanged(getState()); + tryToClose(ss, LOG, WARNING); } @Override - public boolean isRunning() { - return running && socket != null && !socket.isClosed(); + public State getState() { + return state.getState(); } @Override @@ -213,7 +216,7 @@ abstract class TcpPlugin implements DuplexPlugin { @Override public void poll(Collection> properties) { - if (!isRunning()) return; + if (getState() != AVAILABLE) return; backoff.increment(); for (Pair p : properties) { connect(p.getFirst(), p.getSecond()); @@ -232,8 +235,8 @@ abstract class TcpPlugin implements DuplexPlugin { @Override public DuplexTransportConnection createConnection(TransportProperties p) { - if (!isRunning()) return null; - ServerSocket ss = socket; + ServerSocket ss = state.getServerSocket(); + if (ss == null) return null; InterfaceAddress local = getLocalInterfaceAddress(ss.getInetAddress()); if (local == null) { LOG.warning("No interface for server socket"); @@ -366,4 +369,47 @@ abstract class TcpPlugin implements DuplexPlugin { return emptyList(); } } + + @ThreadSafe + @NotNullByDefault + protected static class PluginState { + + @GuardedBy("this") + private boolean started = false, stopped = false; + @GuardedBy("this") + @Nullable + private ServerSocket serverSocket = null; + + synchronized void setStarted() { + started = true; + } + + @Nullable + synchronized ServerSocket setStopped() { + stopped = true; + ServerSocket ss = serverSocket; + serverSocket = null; + return ss; + } + + @Nullable + synchronized ServerSocket getServerSocket() { + return serverSocket; + } + + synchronized boolean setServerSocket(ServerSocket ss) { + if (stopped || serverSocket != null) return false; + serverSocket = ss; + return true; + } + + synchronized void clearServerSocket(ServerSocket ss) { + if (serverSocket == ss) serverSocket = null; + } + + synchronized State getState() { + if (!started || stopped) return DISABLED; + return serverSocket == null ? UNAVAILABLE : AVAILABLE; + } + } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index e23a0da48..d973075f0 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -15,6 +15,7 @@ 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.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.ConnectionHandler; @@ -54,6 +55,9 @@ import java.util.logging.Logger; import java.util.regex.Pattern; import java.util.zip.ZipInputStream; +import javax.annotation.Nullable; +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.ThreadSafe; import javax.net.SocketFactory; import static java.util.Arrays.asList; @@ -65,6 +69,10 @@ import static java.util.logging.Logger.getLogger; import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS; import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY; import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull; +import static org.briarproject.bramble.api.plugin.Plugin.State.AVAILABLE; +import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; +import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING; +import static org.briarproject.bramble.api.plugin.Plugin.State.UNAVAILABLE; import static org.briarproject.bramble.api.plugin.TorConstants.CONTROL_PORT; import static org.briarproject.bramble.api.plugin.TorConstants.ID; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE; @@ -113,16 +121,14 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private final int maxLatency, maxIdleTime, socketTimeout; private final File torDirectory, torFile, geoIpFile, obfs4File, configFile; private final File doneFile, cookieFile; - private final ConnectionStatus connectionStatus; private final AtomicBoolean used = new AtomicBoolean(false); - private volatile ServerSocket socket = null; + protected final PluginState state = new PluginState(); + private volatile Socket controlSocket = null; private volatile TorControlConnection controlConnection = null; private volatile Settings settings = null; - protected volatile boolean running = false; - protected abstract int getProcessId(); protected abstract long getLastUpdateTime(); @@ -159,7 +165,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { configFile = new File(torDirectory, "torrc"); doneFile = new File(torDirectory, "done"); cookieFile = new File(torDirectory, ".tor/control_auth_cookie"); - connectionStatus = new ConnectionStatus(); // Don't execute more than one connection status check at a time connectionStatusExecutor = new PoliteExecutor("TorPlugin", ioExecutor, 1); @@ -258,7 +263,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { // Tell Tor to exit when the control connection is closed controlConnection.takeOwnership(); controlConnection.resetConf(singletonList(OWNER)); - running = true; // Register to receive events from the Tor process controlConnection.setEventHandler(this); controlConnection.setEvents(asList(EVENTS)); @@ -266,11 +270,13 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { String phase = controlConnection.getInfo("status/bootstrap-phase"); if (phase != null && phase.contains("PROGRESS=100")) { LOG.info("Tor has already bootstrapped"); - connectionStatus.setBootstrapped(); + state.setBootstrapped(); } } catch (IOException e) { throw new PluginException(e); } + state.setStarted(); + callback.pluginStateChanged(getState()); // Check whether we're online updateConnectionStatus(networkManager.getNetworkStatus(), batteryManager.isCharging()); @@ -393,11 +399,11 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { tryToClose(ss, LOG, WARNING); return; } - if (!running) { + if (!state.setServerSocket(ss)) { + LOG.info("Closing redundant server socket"); tryToClose(ss, LOG, WARNING); return; } - socket = ss; // Store the port number String localPort = String.valueOf(ss.getLocalPort()); Settings s = new Settings(); @@ -412,7 +418,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } private void publishHiddenService(String port) { - if (!running) return; + if (!state.isRunning()) return; LOG.info("Creating hidden service"); String privKey = settings.get(HS_PRIVKEY); Map portLines = singletonMap(80, "127.0.0.1:" + port); @@ -450,14 +456,16 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } private void acceptContactConnections(ServerSocket ss) { - while (running) { + while (true) { Socket s; try { s = ss.accept(); s.setSoTimeout(socketTimeout); } catch (IOException e) { - // This is expected when the socket is closed - if (LOG.isLoggable(INFO)) LOG.info(e.toString()); + // This is expected when the server socket is closed + // TODO: Check that this is logged at shutdown + LOG.info("Server socket closed"); + state.clearServerSocket(ss); return; } LOG.info("Connection received"); @@ -467,10 +475,10 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } protected void enableNetwork(boolean enable) throws IOException { - if (!running) return; - connectionStatus.enableNetwork(enable); + if (!state.isRunning()) return; + state.enableNetwork(enable); + callback.pluginStateChanged(getState()); controlConnection.setConf("DisableNetwork", enable ? "0" : "1"); - if (!enable) callback.transportDisabled(); } private void enableBridges(boolean enable, boolean needsMeek) @@ -494,9 +502,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @Override public void stop() { - running = false; - tryToClose(socket, LOG, WARNING); - callback.transportDisabled(); + ServerSocket ss = state.setStopped(); + callback.pluginStateChanged(getState()); + tryToClose(ss, LOG, WARNING); if (controlSocket != null && controlConnection != null) { try { LOG.info("Stopping Tor"); @@ -510,8 +518,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } @Override - public boolean isRunning() { - return running && connectionStatus.isConnected(); + public State getState() { + return state.getState(); } @Override @@ -527,7 +535,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @Override public void poll(Collection> properties) { - if (!isRunning()) return; + if (getState() != AVAILABLE) return; backoff.increment(); for (Pair p : properties) { connect(p.getFirst(), p.getSecond()); @@ -546,7 +554,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @Override public DuplexTransportConnection createConnection(TransportProperties p) { - if (!isRunning()) return null; + if (getState() != AVAILABLE) return null; String bestOnion = null; String onion2 = p.get(PROP_ONION_V2); String onion3 = p.get(PROP_ONION_V3); @@ -663,10 +671,10 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @Override public void circuitStatus(String status, String id, String path) { if (status.equals("BUILT") && - connectionStatus.getAndSetCircuitBuilt()) { + state.getAndSetCircuitBuilt()) { + callback.pluginStateChanged(getState()); LOG.info("First circuit built"); backoff.reset(); - if (isRunning()) callback.transportEnabled(); } } @@ -697,9 +705,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { public void message(String severity, String msg) { if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg); if (severity.equals("NOTICE") && msg.startsWith("Bootstrapped 100%")) { - connectionStatus.setBootstrapped(); + state.setBootstrapped(); + callback.pluginStateChanged(getState()); backoff.reset(); - if (isRunning()) callback.transportEnabled(); } } @@ -746,7 +754,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private void updateConnectionStatus(NetworkStatus status, boolean charging) { connectionStatusExecutor.execute(() -> { - if (!running) return; + if (!state.isRunning()) return; boolean online = status.isConnected(); boolean wifi = status.isWifi(); String country = locationUtils.getCurrentCountry(); @@ -762,7 +770,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { if (LOG.isLoggable(INFO)) { LOG.info("Online: " + online + ", wifi: " + wifi); - if ("".equals(country)) LOG.info("Country code unknown"); + if (country.isEmpty()) LOG.info("Country code unknown"); else LOG.info("Country code: " + country); LOG.info("Charging: " + charging); } @@ -810,33 +818,73 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } private void enableConnectionPadding(boolean enable) throws IOException { - if (!running) return; + if (!state.isRunning()) return; controlConnection.setConf("ConnectionPadding", enable ? "1" : "0"); } - private static class ConnectionStatus { + @ThreadSafe + @NotNullByDefault + protected static class PluginState { - // All of the following are locking: this - private boolean networkEnabled = false; - private boolean bootstrapped = false, circuitBuilt = false; + @GuardedBy("this") + private boolean started = false, + stopped = false, + networkInitialised = false, + networkEnabled = false, + bootstrapped = false, + circuitBuilt = false; - private synchronized void setBootstrapped() { + @GuardedBy("this") + @Nullable + private ServerSocket serverSocket = null; + + synchronized void setStarted() { + started = true; + } + + synchronized boolean isRunning() { + return started && !stopped; + } + + @Nullable + synchronized ServerSocket setStopped() { + stopped = true; + ServerSocket ss = serverSocket; + serverSocket = null; + return ss; + } + + synchronized void setBootstrapped() { bootstrapped = true; } - private synchronized boolean getAndSetCircuitBuilt() { + synchronized boolean getAndSetCircuitBuilt() { boolean firstCircuit = !circuitBuilt; circuitBuilt = true; return firstCircuit; } - private synchronized void enableNetwork(boolean enable) { + synchronized void enableNetwork(boolean enable) { + networkInitialised = true; networkEnabled = enable; if (!enable) circuitBuilt = false; } - private synchronized boolean isConnected() { - return networkEnabled && bootstrapped && circuitBuilt; + synchronized boolean setServerSocket(ServerSocket ss) { + if (stopped || serverSocket != null) return false; + serverSocket = ss; + return true; + } + + synchronized void clearServerSocket(ServerSocket ss) { + if (serverSocket == ss) serverSocket = null; + } + + synchronized State getState() { + if (!started || stopped) return DISABLED; + if (!networkInitialised) return ENABLING; + if (!networkEnabled) return UNAVAILABLE; + return bootstrapped && circuitBuilt ? AVAILABLE : ENABLING; } } } diff --git a/bramble-core/src/test/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginTest.java b/bramble-core/src/test/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginTest.java index 73a2f7151..0663df7b1 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginTest.java @@ -4,6 +4,7 @@ import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.keyagreement.KeyAgreementListener; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.Backoff; +import org.briarproject.bramble.api.plugin.Plugin.State; import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.TransportConnectionReader; import org.briarproject.bramble.api.plugin.TransportConnectionWriter; @@ -324,11 +325,7 @@ public class LanTcpPluginTest extends BrambleTestCase { } @Override - public void transportEnabled() { - } - - @Override - public void transportDisabled() { + public void pluginStateChanged(State newState) { } @Override diff --git a/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java b/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java index 16a0d0f31..9b115ad08 100644 --- a/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java +++ b/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java @@ -4,6 +4,7 @@ import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.keyagreement.KeyAgreementListener; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.plugin.ConnectionHandler; import org.briarproject.bramble.api.plugin.PluginCallback; @@ -23,9 +24,16 @@ import java.util.Collection; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.ThreadSafe; + import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; +import static org.briarproject.bramble.api.plugin.Plugin.State.AVAILABLE; +import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; +import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING; +import static org.briarproject.bramble.api.plugin.Plugin.State.UNAVAILABLE; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; @@ -44,8 +52,8 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { private final PluginCallback callback; private final int maxLatency; private final AtomicBoolean used = new AtomicBoolean(false); + private final PluginState state = new PluginState(); - private volatile boolean running = false; private volatile Modem modem = null; ModemPlugin(ModemFactory modemFactory, SerialPortList serialPortList, @@ -75,6 +83,8 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { @Override public void start() throws PluginException { if (used.getAndSet(true)) throw new IllegalStateException(); + state.setStarted(); + callback.pluginStateChanged(getState()); for (String portName : serialPortList.getPortNames()) { if (LOG.isLoggable(INFO)) LOG.info("Trying to initialise modem on " + portName); @@ -83,18 +93,23 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { if (!modem.start()) continue; if (LOG.isLoggable(INFO)) LOG.info("Initialised modem on " + portName); - running = true; + state.setInitialised(); + callback.pluginStateChanged(getState()); return; } catch (IOException e) { logException(LOG, WARNING, e); } } + LOG.warning("Failed to initialised modem"); + state.setFailed(); + callback.pluginStateChanged(getState()); throw new PluginException(); } @Override public void stop() { - running = false; + state.setStopped(); + callback.pluginStateChanged(getState()); if (modem != null) { try { modem.stop(); @@ -105,8 +120,8 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { } @Override - public boolean isRunning() { - return running; + public State getState() { + return state.getState(); } @Override @@ -125,8 +140,8 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { throw new UnsupportedOperationException(); } - private boolean resetModem() { - if (!running) return false; + private void resetModem() { + if (getState() != AVAILABLE) return; for (String portName : serialPortList.getPortNames()) { if (LOG.isLoggable(INFO)) LOG.info("Trying to initialise modem on " + portName); @@ -135,18 +150,19 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { if (!modem.start()) continue; if (LOG.isLoggable(INFO)) LOG.info("Initialised modem on " + portName); - return true; + return; } catch (IOException e) { logException(LOG, WARNING, e); } } - running = false; - return false; + LOG.warning("Failed to initialise modem"); + state.setFailed(); + callback.pluginStateChanged(getState()); } @Override public DuplexTransportConnection createConnection(TransportProperties p) { - if (!running) return null; + if (getState() != AVAILABLE) return null; // Get the ISO 3166 code for the caller's country String fromIso = callback.getLocalProperties().get("iso3166"); if (isNullOrEmpty(fromIso)) return null; @@ -232,4 +248,37 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { if (exception) resetModem(); } } + + @ThreadSafe + @NotNullByDefault + private static class PluginState { + + @GuardedBy("this") + private boolean started = false, + stopped = false, + initialised = false, + failed = false; + + private synchronized void setStarted() { + started = true; + } + + private synchronized void setStopped() { + stopped = true; + } + + private synchronized void setInitialised() { + initialised = true; + } + + private synchronized void setFailed() { + failed = true; + } + + private State getState() { + if (!started || stopped) return DISABLED; + if (failed) return UNAVAILABLE; + return initialised ? AVAILABLE : ENABLING; + } + } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java index 4af7eded3..0cb33503a 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java @@ -30,6 +30,7 @@ import static java.util.concurrent.TimeUnit.DAYS; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; +import static org.briarproject.bramble.api.plugin.Plugin.State.AVAILABLE; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE; import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD; @@ -176,7 +177,7 @@ public class NavDrawerControllerImpl extends DbControllerImpl @Override public boolean isTransportRunning(TransportId transportId) { Plugin plugin = pluginManager.getPlugin(transportId); - return plugin != null && plugin.isRunning(); + return plugin != null && plugin.getState() == AVAILABLE; } } From afc85cdf52689f060c01c3ae082d67c59ecc1b0b Mon Sep 17 00:00:00 2001 From: akwizgran Date: Wed, 15 Jan 2020 17:40:20 +0000 Subject: [PATCH 03/69] Check that server sockets are closed as expected. --- .../briarproject/bramble/plugin/tcp/AndroidLanTcpPlugin.java | 2 +- .../bramble/plugin/bluetooth/BluetoothPlugin.java | 4 ---- .../java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java | 1 - .../java/org/briarproject/bramble/plugin/tor/TorPlugin.java | 1 - 4 files changed, 1 insertion(+), 7 deletions(-) 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 14e18cb51..f39e8914d 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 @@ -147,7 +147,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener { } else if (addrs.isEmpty()) { LOG.info("Not connected to wifi"); socketFactory = SocketFactory.getDefault(); - // TODO: Check that socket was closed when interface went down + // Server socket was closed when wifi interface went down } else { LOG.info("Connected to wifi"); socketFactory = getSocketFactory(); diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java index 2747ca008..8c6b9b9cd 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java @@ -137,9 +137,6 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { void onAdapterDisabled() { LOG.info("Bluetooth disabled"); - // TODO: Is this needed, or will the socket be closed automatically? - SS ss = state.clearServerSocket(); - tryToClose(ss); connectionLimiter.allConnectionsClosed(); callback.pluginStateChanged(getState()); } @@ -241,7 +238,6 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { conn = acceptConnection(ss); } catch (IOException e) { // This is expected when the socket is closed - // TODO: Check that this is logged at shutdown/when BT disabled LOG.info("Server socket closed"); state.clearServerSocket(); return; diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java index 07b4bf782..279879eb5 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java @@ -177,7 +177,6 @@ abstract class TcpPlugin implements DuplexPlugin { s.setSoTimeout(socketTimeout); } catch (IOException e) { // This is expected when the server socket is closed - // TODO: Check that this is logged at shutdown/when LAN disabled LOG.info("Server socket closed"); state.clearServerSocket(ss); callback.pluginStateChanged(getState()); diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index d973075f0..0ffa6ead9 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -463,7 +463,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { s.setSoTimeout(socketTimeout); } catch (IOException e) { // This is expected when the server socket is closed - // TODO: Check that this is logged at shutdown LOG.info("Server socket closed"); state.clearServerSocket(ss); return; From e2d63ac6a4cba19db101303d137fbc6e0378d8c3 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Wed, 15 Jan 2020 17:48:12 +0000 Subject: [PATCH 04/69] If adapter is disabled, forget that we enabled it. --- .../bramble/plugin/bluetooth/AndroidBluetoothPlugin.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/bluetooth/AndroidBluetoothPlugin.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/bluetooth/AndroidBluetoothPlugin.java index 71992283e..560aafd3a 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/bluetooth/AndroidBluetoothPlugin.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/bluetooth/AndroidBluetoothPlugin.java @@ -154,6 +154,12 @@ class AndroidBluetoothPlugin extends BluetoothPlugin { wasEnabledByUs = true; } + @Override + void onAdapterDisabled() { + super.onAdapterDisabled(); + wasEnabledByUs = false; + } + @Override @Nullable String getBluetoothAddress() { From 48dc598ca3d2330fa9ee95a3e49c0bb2295be42d Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 16 Jan 2020 09:47:49 +0000 Subject: [PATCH 05/69] Update tests. --- .../org/briarproject/bramble/plugin/tor/BridgeTest.java | 5 +++-- .../bramble/plugin/tor/TestPluginCallback.java | 7 ++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/BridgeTest.java b/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/BridgeTest.java index b0f47bd86..5947a8265 100644 --- a/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/BridgeTest.java +++ b/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/BridgeTest.java @@ -32,6 +32,7 @@ import javax.net.SocketFactory; import static java.util.Collections.singletonList; import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.logging.Logger.getLogger; +import static org.briarproject.bramble.api.plugin.Plugin.State.AVAILABLE; import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory; import static org.briarproject.bramble.test.TestUtils.getTestDirectory; import static org.briarproject.bramble.test.TestUtils.isOptionalTestEnabled; @@ -141,10 +142,10 @@ public class BridgeTest extends BrambleTestCase { plugin.start(); long start = clock.currentTimeMillis(); while (clock.currentTimeMillis() - start < TIMEOUT) { - if (plugin.isRunning()) return; + if (plugin.getState() == AVAILABLE) return; clock.sleep(500); } - if (!plugin.isRunning()) { + if (plugin.getState() != AVAILABLE) { fail("Could not connect to Tor within timeout."); } } finally { diff --git a/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/TestPluginCallback.java b/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/TestPluginCallback.java index e8b8121da..aeb5a9ee9 100644 --- a/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/TestPluginCallback.java +++ b/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/TestPluginCallback.java @@ -1,6 +1,7 @@ package org.briarproject.bramble.plugin.tor; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.Plugin.State; import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.TransportConnectionReader; import org.briarproject.bramble.api.plugin.TransportConnectionWriter; @@ -30,11 +31,7 @@ public class TestPluginCallback implements PluginCallback { } @Override - public void transportEnabled() { - } - - @Override - public void transportDisabled() { + public void pluginStateChanged(State state) { } @Override From ea288b998b785f764bfdc002f047703ccc664027 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 16 Jan 2020 09:58:12 +0000 Subject: [PATCH 06/69] Rename available/unavailable states. --- .../plugin/tcp/AndroidLanTcpPlugin.java | 12 ++++++------ .../bramble/api/plugin/Plugin.java | 13 +++++-------- .../bramble/plugin/PluginManagerImpl.java | 4 ++-- .../plugin/bluetooth/BluetoothPlugin.java | 18 +++++++++--------- .../bramble/plugin/file/FilePlugin.java | 6 +++--- .../bramble/plugin/tcp/TcpPlugin.java | 10 +++++----- .../bramble/plugin/tor/TorPlugin.java | 12 ++++++------ .../bramble/plugin/modem/ModemPlugin.java | 12 ++++++------ .../bramble/plugin/tor/BridgeTest.java | 6 +++--- .../navdrawer/NavDrawerControllerImpl.java | 4 ++-- 10 files changed, 47 insertions(+), 50 deletions(-) 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 f39e8914d..0e2d59f70 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 @@ -33,8 +33,8 @@ import static android.os.Build.VERSION.SDK_INT; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static java.util.logging.Logger.getLogger; -import static org.briarproject.bramble.api.plugin.Plugin.State.AVAILABLE; -import static org.briarproject.bramble.api.plugin.Plugin.State.UNAVAILABLE; +import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; +import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; @NotNullByDefault class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener { @@ -133,8 +133,8 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener { private void updateConnectionStatus() { connectionStatusExecutor.execute(() -> { State state = getState(); - if (state != AVAILABLE && state != UNAVAILABLE) return; - List addrs = getUsableLocalInetAddresses(); + if (state != ACTIVE && state != INACTIVE) return; + List addrs = getLocalInetAddresses(); if (addrs.contains(WIFI_AP_ADDRESS) || addrs.contains(WIFI_DIRECT_AP_ADDRESS)) { LOG.info("Providing wifi hotspot"); @@ -143,7 +143,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener { // make outgoing connections on API 21+ if another network // has internet access socketFactory = SocketFactory.getDefault(); - if (state == UNAVAILABLE) bind(); + if (state == INACTIVE) bind(); } else if (addrs.isEmpty()) { LOG.info("Not connected to wifi"); socketFactory = SocketFactory.getDefault(); @@ -151,7 +151,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener { } else { LOG.info("Connected to wifi"); socketFactory = getSocketFactory(); - if (state == UNAVAILABLE) bind(); + if (state == INACTIVE) bind(); } }); } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java index b26a37159..d0d9c5ae0 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java @@ -18,23 +18,20 @@ public interface Plugin { DISABLED, /** - * The plugin has been started, has not been stopped, is enabled by - * settings, but can't yet tell whether it can make or receive + * The plugin is being enabled and can't yet make or receive * connections. */ ENABLING, /** - * The plugin has been started, has not been stopped, is enabled by - * settings, and can make or receive connections. + * The plugin is enabled and can make or receive connections. */ - AVAILABLE, + ACTIVE, /** - * The plugin has been started, has not been stopped, is enabled by - * settings, but can't make or receive connections + * The plugin is enabled but can't make or receive connections */ - UNAVAILABLE + INACTIVE } /** diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java index c3ded073b..f6c92aa4e 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java @@ -47,7 +47,7 @@ import static java.util.logging.Level.FINE; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; -import static org.briarproject.bramble.api.plugin.Plugin.State.AVAILABLE; +import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logException; @@ -308,7 +308,7 @@ class PluginManagerImpl implements PluginManager, Service { LOG.info(id + " changed from state " + oldState + " to " + newState); } - if (newState == AVAILABLE) { + if (newState == ACTIVE) { if (!enabled.getAndSet(true)) eventBus.broadcast(new TransportEnabledEvent(id)); } else { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java index 8c6b9b9cd..c34bb771f 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java @@ -50,9 +50,9 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENA import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID; import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES; -import static org.briarproject.bramble.api.plugin.Plugin.State.AVAILABLE; +import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; -import static org.briarproject.bramble.api.plugin.Plugin.State.UNAVAILABLE; +import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; @@ -184,7 +184,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { private void bind() { ioExecutor.execute(() -> { - if (!shouldAllowContactConnections() || getState() != AVAILABLE) + if (!shouldAllowContactConnections() || getState() != ACTIVE) return; // Bind a server socket to accept connections from contacts SS ss; @@ -275,7 +275,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { @Override public void poll(Collection> properties) { - if (!shouldAllowContactConnections() || getState() != AVAILABLE) return; + if (!shouldAllowContactConnections() || getState() != ACTIVE) return; backoff.increment(); for (Pair p : properties) { connect(p.getFirst(), p.getSecond()); @@ -329,7 +329,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { @Override public DuplexTransportConnection createConnection(TransportProperties p) { - if (!shouldAllowContactConnections() || getState() != AVAILABLE) + if (!shouldAllowContactConnections() || getState() != ACTIVE) return null; if (!connectionLimiter.canOpenContactConnection()) return null; String address = p.get(PROP_ADDRESS); @@ -348,7 +348,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { @Override public KeyAgreementListener createKeyAgreementListener(byte[] commitment) { - if (getState() != AVAILABLE) return null; + if (getState() != ACTIVE) return null; // No truncation necessary because COMMIT_LENGTH = 16 String uuid = UUID.nameUUIDFromBytes(commitment).toString(); if (LOG.isLoggable(INFO)) LOG.info("Key agreement UUID " + uuid); @@ -360,7 +360,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { logException(LOG, WARNING, e); return null; } - if (getState() != AVAILABLE) { + if (getState() != ACTIVE) { tryToClose(ss); return null; } @@ -374,7 +374,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { @Override public DuplexTransportConnection createKeyAgreementConnection( byte[] commitment, BdfList descriptor) { - if (getState() != AVAILABLE) return null; + if (getState() != ACTIVE) return null; // No truncation necessary because COMMIT_LENGTH = 16 String uuid = UUID.nameUUIDFromBytes(commitment).toString(); DuplexTransportConnection conn; @@ -509,7 +509,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { synchronized State getState() { if (!started || stopped) return DISABLED; - return isAdapterEnabled() ? AVAILABLE : UNAVAILABLE; + return isAdapterEnabled() ? ACTIVE : INACTIVE; } } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/file/FilePlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/file/FilePlugin.java index d846fd2ae..8a2673a7a 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/file/FilePlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/file/FilePlugin.java @@ -16,7 +16,7 @@ import java.util.logging.Logger; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.plugin.FileConstants.PROP_PATH; -import static org.briarproject.bramble.api.plugin.Plugin.State.AVAILABLE; +import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; @@ -46,7 +46,7 @@ abstract class FilePlugin implements SimplexPlugin { @Override public TransportConnectionReader createReader(TransportProperties p) { - if (getState() != AVAILABLE) return null; + if (getState() != ACTIVE) return null; String path = p.get(PROP_PATH); if (isNullOrEmpty(path)) return null; try { @@ -61,7 +61,7 @@ abstract class FilePlugin implements SimplexPlugin { @Override public TransportConnectionWriter createWriter(TransportProperties p) { - if (getState() != AVAILABLE) return null; + if (getState() != ACTIVE) return null; String path = p.get(PROP_PATH); if (isNullOrEmpty(path)) return null; try { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java index 279879eb5..627e1d680 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java @@ -43,9 +43,9 @@ import static java.util.Collections.list; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; -import static org.briarproject.bramble.api.plugin.Plugin.State.AVAILABLE; +import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; -import static org.briarproject.bramble.api.plugin.Plugin.State.UNAVAILABLE; +import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; import static org.briarproject.bramble.util.IoUtils.tryToClose; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress; @@ -129,7 +129,7 @@ abstract class TcpPlugin implements DuplexPlugin { protected void bind() { bindExecutor.execute(() -> { - if (getState() != UNAVAILABLE) return; + if (getState() != INACTIVE) return; ServerSocket ss = null; for (InetSocketAddress addr : getLocalSocketAddresses()) { try { @@ -215,7 +215,7 @@ abstract class TcpPlugin implements DuplexPlugin { @Override public void poll(Collection> properties) { - if (getState() != AVAILABLE) return; + if (getState() != ACTIVE) return; backoff.increment(); for (Pair p : properties) { connect(p.getFirst(), p.getSecond()); @@ -408,7 +408,7 @@ abstract class TcpPlugin implements DuplexPlugin { synchronized State getState() { if (!started || stopped) return DISABLED; - return serverSocket == null ? UNAVAILABLE : AVAILABLE; + return serverSocket == null ? INACTIVE : ACTIVE; } } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index 0ffa6ead9..1b09fcfb5 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -69,10 +69,10 @@ import static java.util.logging.Logger.getLogger; import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS; import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY; import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull; -import static org.briarproject.bramble.api.plugin.Plugin.State.AVAILABLE; +import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING; -import static org.briarproject.bramble.api.plugin.Plugin.State.UNAVAILABLE; +import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; import static org.briarproject.bramble.api.plugin.TorConstants.CONTROL_PORT; import static org.briarproject.bramble.api.plugin.TorConstants.ID; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE; @@ -534,7 +534,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @Override public void poll(Collection> properties) { - if (getState() != AVAILABLE) return; + if (getState() != ACTIVE) return; backoff.increment(); for (Pair p : properties) { connect(p.getFirst(), p.getSecond()); @@ -553,7 +553,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @Override public DuplexTransportConnection createConnection(TransportProperties p) { - if (getState() != AVAILABLE) return null; + if (getState() != ACTIVE) return null; String bestOnion = null; String onion2 = p.get(PROP_ONION_V2); String onion3 = p.get(PROP_ONION_V3); @@ -882,8 +882,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { synchronized State getState() { if (!started || stopped) return DISABLED; if (!networkInitialised) return ENABLING; - if (!networkEnabled) return UNAVAILABLE; - return bootstrapped && circuitBuilt ? AVAILABLE : ENABLING; + if (!networkEnabled) return INACTIVE; + return bootstrapped && circuitBuilt ? ACTIVE : ENABLING; } } } diff --git a/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java b/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java index 9b115ad08..aebf61fd6 100644 --- a/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java +++ b/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java @@ -30,10 +30,10 @@ import javax.annotation.concurrent.ThreadSafe; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; -import static org.briarproject.bramble.api.plugin.Plugin.State.AVAILABLE; +import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING; -import static org.briarproject.bramble.api.plugin.Plugin.State.UNAVAILABLE; +import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; @@ -141,7 +141,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { } private void resetModem() { - if (getState() != AVAILABLE) return; + if (getState() != ACTIVE) return; for (String portName : serialPortList.getPortNames()) { if (LOG.isLoggable(INFO)) LOG.info("Trying to initialise modem on " + portName); @@ -162,7 +162,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { @Override public DuplexTransportConnection createConnection(TransportProperties p) { - if (getState() != AVAILABLE) return null; + if (getState() != ACTIVE) return null; // Get the ISO 3166 code for the caller's country String fromIso = callback.getLocalProperties().get("iso3166"); if (isNullOrEmpty(fromIso)) return null; @@ -277,8 +277,8 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { private State getState() { if (!started || stopped) return DISABLED; - if (failed) return UNAVAILABLE; - return initialised ? AVAILABLE : ENABLING; + if (failed) return INACTIVE; + return initialised ? ACTIVE : ENABLING; } } } diff --git a/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/BridgeTest.java b/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/BridgeTest.java index 5947a8265..7439321fd 100644 --- a/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/BridgeTest.java +++ b/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/BridgeTest.java @@ -32,7 +32,7 @@ import javax.net.SocketFactory; import static java.util.Collections.singletonList; import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.logging.Logger.getLogger; -import static org.briarproject.bramble.api.plugin.Plugin.State.AVAILABLE; +import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory; import static org.briarproject.bramble.test.TestUtils.getTestDirectory; import static org.briarproject.bramble.test.TestUtils.isOptionalTestEnabled; @@ -142,10 +142,10 @@ public class BridgeTest extends BrambleTestCase { plugin.start(); long start = clock.currentTimeMillis(); while (clock.currentTimeMillis() - start < TIMEOUT) { - if (plugin.getState() == AVAILABLE) return; + if (plugin.getState() == ACTIVE) return; clock.sleep(500); } - if (plugin.getState() != AVAILABLE) { + if (plugin.getState() != ACTIVE) { fail("Could not connect to Tor within timeout."); } } finally { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java index 0cb33503a..a927fe409 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java @@ -30,7 +30,7 @@ import static java.util.concurrent.TimeUnit.DAYS; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; -import static org.briarproject.bramble.api.plugin.Plugin.State.AVAILABLE; +import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE; import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD; @@ -177,7 +177,7 @@ public class NavDrawerControllerImpl extends DbControllerImpl @Override public boolean isTransportRunning(TransportId transportId) { Plugin plugin = pluginManager.getPlugin(transportId); - return plugin != null && plugin.getState() == AVAILABLE; + return plugin != null && plugin.getState() == ACTIVE; } } From 43a91e2e576df1ddc810bd72e0dfec832eedc398 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 16 Jan 2020 11:05:02 +0000 Subject: [PATCH 07/69] Fix test expectations. --- .../bramble/plugin/modem/ModemPluginTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/bramble-java/src/test/java/org/briarproject/bramble/plugin/modem/ModemPluginTest.java b/bramble-java/src/test/java/org/briarproject/bramble/plugin/modem/ModemPluginTest.java index df03d3abf..4e3ae0684 100644 --- a/bramble-java/src/test/java/org/briarproject/bramble/plugin/modem/ModemPluginTest.java +++ b/bramble-java/src/test/java/org/briarproject/bramble/plugin/modem/ModemPluginTest.java @@ -9,6 +9,8 @@ import org.junit.Test; import java.io.IOException; +import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; +import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -33,6 +35,7 @@ public class ModemPluginTest extends BrambleMockTestCase { @Test public void testModemCreation() throws Exception { context.checking(new Expectations() {{ + oneOf(callback).pluginStateChanged(ENABLING); oneOf(serialPortList).getPortNames(); will(returnValue(new String[] {"foo", "bar", "baz"})); // First call to createModem() returns false @@ -50,6 +53,7 @@ public class ModemPluginTest extends BrambleMockTestCase { will(returnValue(modem)); oneOf(modem).start(); will(returnValue(true)); + oneOf(callback).pluginStateChanged(ACTIVE); }}); plugin.start(); @@ -65,12 +69,14 @@ public class ModemPluginTest extends BrambleMockTestCase { context.checking(new Expectations() {{ // start() + oneOf(callback).pluginStateChanged(ENABLING); oneOf(serialPortList).getPortNames(); will(returnValue(new String[] {"foo"})); oneOf(modemFactory).createModem(plugin, "foo"); will(returnValue(modem)); oneOf(modem).start(); will(returnValue(true)); + oneOf(callback).pluginStateChanged(ACTIVE); // createConnection() oneOf(callback).getLocalProperties(); will(returnValue(local)); @@ -93,12 +99,14 @@ public class ModemPluginTest extends BrambleMockTestCase { context.checking(new Expectations() {{ // start() + oneOf(callback).pluginStateChanged(ENABLING); oneOf(serialPortList).getPortNames(); will(returnValue(new String[] {"foo"})); oneOf(modemFactory).createModem(plugin, "foo"); will(returnValue(modem)); oneOf(modem).start(); will(returnValue(true)); + oneOf(callback).pluginStateChanged(ACTIVE); // createConnection() oneOf(callback).getLocalProperties(); will(returnValue(local)); @@ -121,12 +129,14 @@ public class ModemPluginTest extends BrambleMockTestCase { context.checking(new Expectations() {{ // start() + oneOf(callback).pluginStateChanged(ENABLING); oneOf(serialPortList).getPortNames(); will(returnValue(new String[] {"foo"})); oneOf(modemFactory).createModem(plugin, "foo"); will(returnValue(modem)); oneOf(modem).start(); will(returnValue(true)); + oneOf(callback).pluginStateChanged(ACTIVE); // createConnection() oneOf(callback).getLocalProperties(); will(returnValue(local)); From ecea2c587daf30ce179f50f16883cd2f6ce7c686 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 16 Jan 2020 11:05:36 +0000 Subject: [PATCH 08/69] Add method for getting reason why plugin is disabled. --- .../bramble/api/plugin/Plugin.java | 17 ++ .../bramble/api/plugin/TorConstants.java | 4 + .../plugin/bluetooth/BluetoothPlugin.java | 5 + .../bramble/plugin/tcp/TcpPlugin.java | 5 + .../bramble/plugin/tor/TorPlugin.java | 181 ++++++++++++------ .../bramble/plugin/modem/ModemPlugin.java | 5 + 6 files changed, 163 insertions(+), 54 deletions(-) diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java index d0d9c5ae0..b7247967c 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java @@ -34,6 +34,13 @@ public interface Plugin { INACTIVE } + /** + * Reason code returned by {@link #getReasonDisabled()} ()} to indicate + * that the plugin is disabled because it has not been started or has been + * stopped. + */ + int REASON_STARTING_STOPPING = 0; + /** * Returns the plugin's transport identifier. */ @@ -64,6 +71,16 @@ public interface Plugin { */ State getState(); + /** + * Returns an integer code indicating why the plugin is + * {@link State#DISABLED disabled}, or -1 if the plugin is not disabled. + *

+ * The codes used are plugin-specific, except the generic code + * {@link #REASON_STARTING_STOPPING}, which may be used by + * any plugin. + */ + int getReasonDisabled(); + /** * Returns true if the plugin should be polled periodically to attempt to * establish connections. diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java index 6be7582a1..2d808eaa9 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java @@ -23,4 +23,8 @@ public interface TorConstants { int PREF_TOR_NETWORK_WITH_BRIDGES = 2; int PREF_TOR_NETWORK_NEVER = 3; + int REASON_USER = 1; + int REASON_BATTERY = 2; + int REASON_MOBILE_DATA = 3; + int REASON_COUNTRY_BLOCKED = 4; } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java index c34bb771f..f4e8bb128 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java @@ -262,6 +262,11 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { return state.getState(); } + @Override + public int getReasonDisabled() { + return getState() == DISABLED ? REASON_STARTING_STOPPING : -1; + } + @Override public boolean shouldPoll() { return true; diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java index 627e1d680..e92314ee5 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java @@ -202,6 +202,11 @@ abstract class TcpPlugin implements DuplexPlugin { return state.getState(); } + @Override + public int getReasonDisabled() { + return getState() == DISABLED ? REASON_STARTING_STOPPING : -1; + } + @Override public boolean shouldPoll() { return true; diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index 1b09fcfb5..a32eb7b2c 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -57,6 +57,7 @@ import java.util.zip.ZipInputStream; import javax.annotation.Nullable; import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.ThreadSafe; import javax.net.SocketFactory; @@ -84,6 +85,10 @@ import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ONLY_WHE import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT; import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V2; import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V3; +import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY; +import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED; +import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA; +import static org.briarproject.bramble.api.plugin.TorConstants.REASON_USER; import static org.briarproject.bramble.plugin.tor.TorRendezvousCrypto.SEED_BYTES; import static org.briarproject.bramble.util.IoUtils.copyAndClose; import static org.briarproject.bramble.util.IoUtils.tryToClose; @@ -521,6 +526,11 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { return state.getState(); } + @Override + public int getReasonDisabled() { + return state.getReasonDisabled(); + } + @Override public boolean shouldPoll() { return true; @@ -754,62 +764,91 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { boolean charging) { connectionStatusExecutor.execute(() -> { if (!state.isRunning()) return; - boolean online = status.isConnected(); - boolean wifi = status.isWifi(); - String country = locationUtils.getCurrentCountry(); - boolean blocked = - circumventionProvider.isTorProbablyBlocked(country); - int network = settings.getInt(PREF_TOR_NETWORK, - PREF_TOR_NETWORK_AUTOMATIC); - boolean useMobile = settings.getBoolean(PREF_TOR_MOBILE, true); - boolean onlyWhenCharging = - settings.getBoolean(PREF_TOR_ONLY_WHEN_CHARGING, false); - boolean bridgesWork = circumventionProvider.doBridgesWork(country); - boolean automatic = network == PREF_TOR_NETWORK_AUTOMATIC; + NetworkConfig config = getNetworkConfig(status, charging); + state.setDisabledBySettings(config.disabledBySettings, + config.reasonDisabled); + callback.pluginStateChanged(getState()); + applyNetworkConfig(config); + }); + } - if (LOG.isLoggable(INFO)) { - LOG.info("Online: " + online + ", wifi: " + wifi); - if (country.isEmpty()) LOG.info("Country code unknown"); - else LOG.info("Country code: " + country); - LOG.info("Charging: " + charging); + private NetworkConfig getNetworkConfig(NetworkStatus status, + boolean charging) { + boolean online = status.isConnected(); + boolean wifi = status.isWifi(); + String country = locationUtils.getCurrentCountry(); + boolean blocked = circumventionProvider.isTorProbablyBlocked(country); + int network = + settings.getInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_AUTOMATIC); + boolean useMobile = settings.getBoolean(PREF_TOR_MOBILE, true); + boolean onlyWhenCharging = + settings.getBoolean(PREF_TOR_ONLY_WHEN_CHARGING, false); + boolean bridgesWork = circumventionProvider.doBridgesWork(country); + boolean automatic = network == PREF_TOR_NETWORK_AUTOMATIC; + + if (LOG.isLoggable(INFO)) { + LOG.info("Online: " + online + ", wifi: " + wifi); + if (country.isEmpty()) LOG.info("Country code unknown"); + else LOG.info("Country code: " + country); + LOG.info("Charging: " + charging); + } + + boolean enableNetwork = false, enableBridges = false; + boolean useMeek = false, enableConnectionPadding = false; + boolean disabledBySettings = false; + int reasonDisabled = REASON_STARTING_STOPPING; + + if (!online) { + LOG.info("Disabling network, device is offline"); + } else if (network == PREF_TOR_NETWORK_NEVER) { + LOG.info("Disabling network, user has disabled Tor"); + disabledBySettings = true; + reasonDisabled = REASON_USER; + } else if (!charging && onlyWhenCharging) { + LOG.info("Disabling network, device is on battery"); + disabledBySettings = true; + reasonDisabled = REASON_BATTERY; + } else if (!useMobile && !wifi) { + LOG.info("Disabling network, device is using mobile data"); + disabledBySettings = true; + reasonDisabled = REASON_MOBILE_DATA; + } else if (automatic && blocked && !bridgesWork) { + LOG.info("Disabling network, country is blocked"); + disabledBySettings = true; + reasonDisabled = REASON_COUNTRY_BLOCKED; + } else if (network == PREF_TOR_NETWORK_WITH_BRIDGES || + (automatic && bridgesWork)) { + if (circumventionProvider.needsMeek(country)) { + LOG.info("Enabling network, using meek bridges"); + enableBridges = true; + useMeek = true; + } else { + LOG.info("Enabling network, using obfs4 bridges"); + enableBridges = true; } + enableNetwork = true; + } else { + LOG.info("Enabling network, not using bridges"); + enableNetwork = true; + } + if (online && wifi && charging) { + LOG.info("Enabling connection padding"); + enableConnectionPadding = true; + } else { + LOG.info("Disabling connection padding"); + } + + return new NetworkConfig(enableNetwork, enableBridges, useMeek, + enableConnectionPadding, disabledBySettings, reasonDisabled); + } + + private void applyNetworkConfig(NetworkConfig config) { + connectionStatusExecutor.execute(() -> { try { - if (!online) { - LOG.info("Disabling network, device is offline"); - enableNetwork(false); - } else if (!charging && onlyWhenCharging) { - LOG.info("Disabling network, device is on battery"); - enableNetwork(false); - } else if (network == PREF_TOR_NETWORK_NEVER || - (!useMobile && !wifi)) { - LOG.info("Disabling network, device is using mobile data"); - enableNetwork(false); - } else if (automatic && blocked && !bridgesWork) { - LOG.info("Disabling network, country is blocked"); - enableNetwork(false); - } else if (network == PREF_TOR_NETWORK_WITH_BRIDGES || - (automatic && bridgesWork)) { - if (circumventionProvider.needsMeek(country)) { - LOG.info("Enabling network, using meek bridges"); - enableBridges(true, true); - } else { - LOG.info("Enabling network, using obfs4 bridges"); - enableBridges(true, false); - } - enableNetwork(true); - } else { - LOG.info("Enabling network, not using bridges"); - enableBridges(false, false); - enableNetwork(true); - } - if (online && wifi && charging) { - LOG.info("Enabling connection padding"); - enableConnectionPadding(true); - } else { - LOG.info("Disabling connection padding"); - enableConnectionPadding(false); - } + enableBridges(config.enableBridges, config.useMeek); + enableNetwork(config.enableNetwork); + enableConnectionPadding(config.enableConnectionPadding); } catch (IOException e) { logException(LOG, WARNING, e); } @@ -821,6 +860,26 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { controlConnection.setConf("ConnectionPadding", enable ? "1" : "0"); } + @Immutable + @NotNullByDefault + private static class NetworkConfig { + + private final boolean enableNetwork, enableBridges, useMeek; + private final boolean enableConnectionPadding, disabledBySettings; + private final int reasonDisabled; + + private NetworkConfig(boolean enableNetwork, boolean enableBridges, + boolean useMeek, boolean enableConnectionPadding, + boolean disabledBySettings, int reasonDisabled) { + this.enableNetwork = enableNetwork; + this.enableBridges = enableBridges; + this.useMeek = useMeek; + this.enableConnectionPadding = enableConnectionPadding; + this.disabledBySettings = disabledBySettings; + this.reasonDisabled = reasonDisabled; + } + } + @ThreadSafe @NotNullByDefault protected static class PluginState { @@ -831,7 +890,11 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { networkInitialised = false, networkEnabled = false, bootstrapped = false, - circuitBuilt = false; + circuitBuilt = false, + disabledBySettings = false; + + @GuardedBy("this") + private int reasonDisabled = REASON_STARTING_STOPPING; @GuardedBy("this") @Nullable @@ -869,6 +932,12 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { if (!enable) circuitBuilt = false; } + synchronized void setDisabledBySettings(boolean disabledBySettings, + int reasonDisabled) { + this.disabledBySettings = disabledBySettings; + this.reasonDisabled = reasonDisabled; + } + synchronized boolean setServerSocket(ServerSocket ss) { if (stopped || serverSocket != null) return false; serverSocket = ss; @@ -880,10 +949,14 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } synchronized State getState() { - if (!started || stopped) return DISABLED; + if (!started || stopped || disabledBySettings) return DISABLED; if (!networkInitialised) return ENABLING; if (!networkEnabled) return INACTIVE; return bootstrapped && circuitBuilt ? ACTIVE : ENABLING; } + + synchronized int getReasonDisabled() { + return getState() == DISABLED ? reasonDisabled : -1; + } } } diff --git a/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java b/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java index aebf61fd6..89a11ff2f 100644 --- a/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java +++ b/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java @@ -124,6 +124,11 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { return state.getState(); } + @Override + public int getReasonDisabled() { + return getState() == DISABLED ? REASON_STARTING_STOPPING : -1; + } + @Override public boolean shouldPoll() { return false; From d30b250389936ff24679af4638434b0fe32fa13f Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 16 Jan 2020 11:35:32 +0000 Subject: [PATCH 09/69] Ensure server socket is closed. --- .../plugin/tcp/AndroidLanTcpPlugin.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) 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 0e2d59f70..e9ef6fe41 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 @@ -32,9 +32,11 @@ import static android.net.ConnectivityManager.TYPE_WIFI; import static android.os.Build.VERSION.SDK_INT; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; +import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; +import static org.briarproject.bramble.util.IoUtils.tryToClose; @NotNullByDefault class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener { @@ -132,8 +134,8 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener { private void updateConnectionStatus() { connectionStatusExecutor.execute(() -> { - State state = getState(); - if (state != ACTIVE && state != INACTIVE) return; + State s = getState(); + if (s != ACTIVE && s != INACTIVE) return; List addrs = getLocalInetAddresses(); if (addrs.contains(WIFI_AP_ADDRESS) || addrs.contains(WIFI_DIRECT_AP_ADDRESS)) { @@ -143,15 +145,21 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener { // make outgoing connections on API 21+ if another network // has internet access socketFactory = SocketFactory.getDefault(); - if (state == INACTIVE) bind(); + if (s == INACTIVE) bind(); } else if (addrs.isEmpty()) { LOG.info("Not connected to wifi"); socketFactory = SocketFactory.getDefault(); - // Server socket was closed when wifi interface went down + // Server socket may not have been closed automatically when + // interface was taken down. Socket will be cleared and state + // updated in acceptContactConnections() + if (s == ACTIVE) { + LOG.info("Closing server socket"); + tryToClose(state.getServerSocket(), LOG, WARNING); + } } else { LOG.info("Connected to wifi"); socketFactory = getSocketFactory(); - if (state == INACTIVE) bind(); + if (s == INACTIVE) bind(); } }); } From c554847b543b34bb227c56343333dc7e0761b52c Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 16 Jan 2020 11:54:28 +0000 Subject: [PATCH 10/69] Add TransportStateEvent, rename existing events. --- ...edEvent.java => TransportActiveEvent.java} | 8 +++-- ...Event.java => TransportInactiveEvent.java} | 8 +++-- .../api/plugin/event/TransportStateEvent.java | 32 +++++++++++++++++++ .../bramble/plugin/PluginManagerImpl.java | 15 ++++----- .../bramble/plugin/PollerImpl.java | 16 +++++----- .../rendezvous/RendezvousPollerImpl.java | 12 +++---- .../bramble/reporting/DevReporterImpl.java | 6 ++-- .../bramble/plugin/PollerImplTest.java | 18 +++++------ .../rendezvous/RendezvousPollerImplTest.java | 30 ++++++++--------- .../navdrawer/NavDrawerControllerImpl.java | 16 +++++----- .../briar/feed/FeedManagerImpl.java | 6 ++-- 11 files changed, 101 insertions(+), 66 deletions(-) rename bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/{TransportDisabledEvent.java => TransportActiveEvent.java} (64%) rename bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/{TransportEnabledEvent.java => TransportInactiveEvent.java} (64%) create mode 100644 bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/TransportStateEvent.java diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/TransportDisabledEvent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/TransportActiveEvent.java similarity index 64% rename from bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/TransportDisabledEvent.java rename to bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/TransportActiveEvent.java index 1d415ca9f..de9c49c90 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/TransportDisabledEvent.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/TransportActiveEvent.java @@ -2,20 +2,22 @@ package org.briarproject.bramble.api.plugin.event; import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.Plugin.State; import org.briarproject.bramble.api.plugin.TransportId; import javax.annotation.concurrent.Immutable; /** - * An event that is broadcast when a transport is disabled. + * An event that is broadcast when a plugin enters the {@link State#ACTIVE} + * state. */ @Immutable @NotNullByDefault -public class TransportDisabledEvent extends Event { +public class TransportActiveEvent extends Event { private final TransportId transportId; - public TransportDisabledEvent(TransportId transportId) { + public TransportActiveEvent(TransportId transportId) { this.transportId = transportId; } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/TransportEnabledEvent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/TransportInactiveEvent.java similarity index 64% rename from bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/TransportEnabledEvent.java rename to bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/TransportInactiveEvent.java index 7064c9b9a..e2167c0f5 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/TransportEnabledEvent.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/TransportInactiveEvent.java @@ -2,20 +2,22 @@ package org.briarproject.bramble.api.plugin.event; import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.Plugin.State; import org.briarproject.bramble.api.plugin.TransportId; import javax.annotation.concurrent.Immutable; /** - * An event that is broadcast when a transport is enabled. + * An event that is broadcast when a plugin leaves the {@link State#ACTIVE} + * state. */ @Immutable @NotNullByDefault -public class TransportEnabledEvent extends Event { +public class TransportInactiveEvent extends Event { private final TransportId transportId; - public TransportEnabledEvent(TransportId transportId) { + public TransportInactiveEvent(TransportId transportId) { this.transportId = transportId; } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/TransportStateEvent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/TransportStateEvent.java new file mode 100644 index 000000000..376b5303f --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/TransportStateEvent.java @@ -0,0 +1,32 @@ +package org.briarproject.bramble.api.plugin.event; + +import org.briarproject.bramble.api.event.Event; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.Plugin.State; +import org.briarproject.bramble.api.plugin.TransportId; + +import javax.annotation.concurrent.Immutable; + +/** + * An event that is broadcast when the {@link State state} of a plugin changes. + */ +@Immutable +@NotNullByDefault +public class TransportStateEvent extends Event { + + private final TransportId transportId; + private final State state; + + public TransportStateEvent(TransportId transportId, State state) { + this.transportId = transportId; + this.state = state; + } + + public TransportId getTransportId() { + return transportId; + } + + public State getState() { + return state; + } +} diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java index f6c92aa4e..36a7d8252 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java @@ -19,8 +19,9 @@ import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; -import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent; -import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent; +import org.briarproject.bramble.api.plugin.event.TransportActiveEvent; +import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent; +import org.briarproject.bramble.api.plugin.event.TransportStateEvent; import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.properties.TransportProperties; @@ -256,7 +257,6 @@ class PluginManagerImpl implements PluginManager, Service { private final TransportId id; private final AtomicReference state = new AtomicReference<>(DISABLED); - private final AtomicBoolean enabled = new AtomicBoolean(false); private Callback(TransportId id) { this.id = id; @@ -308,12 +308,11 @@ class PluginManagerImpl implements PluginManager, Service { LOG.info(id + " changed from state " + oldState + " to " + newState); } + eventBus.broadcast(new TransportStateEvent(id, newState)); if (newState == ACTIVE) { - if (!enabled.getAndSet(true)) - eventBus.broadcast(new TransportEnabledEvent(id)); - } else { - if (enabled.getAndSet(false)) - eventBus.broadcast(new TransportDisabledEvent(id)); + eventBus.broadcast(new TransportActiveEvent(id)); + } else if (oldState == ACTIVE) { + eventBus.broadcast(new TransportInactiveEvent(id)); } } else { // TODO: Remove diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PollerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PollerImpl.java index 1aeb66172..58637ddca 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PollerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PollerImpl.java @@ -20,8 +20,8 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent; import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent; -import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent; -import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent; +import org.briarproject.bramble.api.plugin.event.TransportActiveEvent; +import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent; import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin; import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportPropertyManager; @@ -106,13 +106,13 @@ class PollerImpl implements Poller, EventListener { ConnectionOpenedEvent c = (ConnectionOpenedEvent) e; // Reschedule polling, the polling interval may have decreased reschedule(c.getTransportId()); - } else if (e instanceof TransportEnabledEvent) { - TransportEnabledEvent t = (TransportEnabledEvent) e; - // Poll the newly enabled transport + } else if (e instanceof TransportActiveEvent) { + TransportActiveEvent t = (TransportActiveEvent) e; + // Poll the newly activated transport pollNow(t.getTransportId()); - } else if (e instanceof TransportDisabledEvent) { - TransportDisabledEvent t = (TransportDisabledEvent) e; - // Cancel polling for the disabled transport + } else if (e instanceof TransportInactiveEvent) { + TransportInactiveEvent t = (TransportInactiveEvent) e; + // Cancel polling for the deactivated transport cancel(t.getTransportId()); } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/rendezvous/RendezvousPollerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/rendezvous/RendezvousPollerImpl.java index 8e41e9b64..9f6a0cb24 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/rendezvous/RendezvousPollerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/rendezvous/RendezvousPollerImpl.java @@ -31,8 +31,8 @@ import org.briarproject.bramble.api.plugin.TransportConnectionWriter; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; -import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent; -import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent; +import org.briarproject.bramble.api.plugin.event.TransportActiveEvent; +import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent; import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.rendezvous.KeyMaterialSource; import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint; @@ -269,11 +269,11 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener { } else if (e instanceof PendingContactRemovedEvent) { PendingContactRemovedEvent p = (PendingContactRemovedEvent) e; removePendingContactAsync(p.getId()); - } else if (e instanceof TransportEnabledEvent) { - TransportEnabledEvent t = (TransportEnabledEvent) e; + } else if (e instanceof TransportActiveEvent) { + TransportActiveEvent t = (TransportActiveEvent) e; addTransportAsync(t.getTransportId()); - } else if (e instanceof TransportDisabledEvent) { - TransportDisabledEvent t = (TransportDisabledEvent) e; + } else if (e instanceof TransportInactiveEvent) { + TransportInactiveEvent t = (TransportInactiveEvent) e; removeTransportAsync(t.getTransportId()); } else if (e instanceof RendezvousConnectionOpenedEvent) { RendezvousConnectionOpenedEvent r = diff --git a/bramble-core/src/main/java/org/briarproject/bramble/reporting/DevReporterImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/reporting/DevReporterImpl.java index f03d4f730..228569fa2 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/reporting/DevReporterImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/reporting/DevReporterImpl.java @@ -6,7 +6,7 @@ import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.TorConstants; -import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent; +import org.briarproject.bramble.api.plugin.event.TransportActiveEvent; import org.briarproject.bramble.api.reporting.DevConfig; import org.briarproject.bramble.api.reporting.DevReporter; import org.briarproject.bramble.util.IoUtils; @@ -92,8 +92,8 @@ class DevReporterImpl implements DevReporter, EventListener { @Override public void eventOccurred(Event e) { - if (e instanceof TransportEnabledEvent) { - TransportEnabledEvent t = (TransportEnabledEvent) e; + if (e instanceof TransportActiveEvent) { + TransportActiveEvent t = (TransportActiveEvent) e; if (t.getTransportId().equals(TorConstants.ID)) ioExecutor.execute(this::sendReports); } diff --git a/bramble-core/src/test/java/org/briarproject/bramble/plugin/PollerImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/plugin/PollerImplTest.java index cf29a7a0e..5c65311e0 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/plugin/PollerImplTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/plugin/PollerImplTest.java @@ -13,8 +13,8 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent; import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent; -import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent; -import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent; +import org.briarproject.bramble.api.plugin.event.TransportActiveEvent; +import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent; import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin; import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportPropertyManager; @@ -331,7 +331,7 @@ public class PollerImplTest extends BrambleMockTestCase { } @Test - public void testPollsOnTransportEnabled() throws Exception { + public void testPollsOnTransportActivated() throws Exception { DuplexPlugin plugin = context.mock(DuplexPlugin.class); context.checking(new Expectations() {{ @@ -370,7 +370,7 @@ public class PollerImplTest extends BrambleMockTestCase { pairOf(equal(properties), any(ConnectionHandler.class))))); }}); - poller.eventOccurred(new TransportEnabledEvent(transportId)); + poller.eventOccurred(new TransportActiveEvent(transportId)); } @Test @@ -411,11 +411,11 @@ public class PollerImplTest extends BrambleMockTestCase { // All contacts are connected, so don't poll the plugin }}); - poller.eventOccurred(new TransportEnabledEvent(transportId)); + poller.eventOccurred(new TransportActiveEvent(transportId)); } @Test - public void testCancelsPollingOnTransportDisabled() { + public void testCancelsPollingOnTransportDeactivated() { Plugin plugin = context.mock(Plugin.class); context.checking(new Expectations() {{ @@ -433,12 +433,12 @@ public class PollerImplTest extends BrambleMockTestCase { oneOf(scheduler).schedule(with(any(Runnable.class)), with(0L), with(MILLISECONDS)); will(returnValue(future)); - // The plugin is disabled before the task runs - cancel the task + // The plugin is deactivated before the task runs - cancel the task oneOf(future).cancel(false); }}); - poller.eventOccurred(new TransportEnabledEvent(transportId)); - poller.eventOccurred(new TransportDisabledEvent(transportId)); + poller.eventOccurred(new TransportActiveEvent(transportId)); + poller.eventOccurred(new TransportInactiveEvent(transportId)); } private void expectReschedule(Plugin plugin) { diff --git a/bramble-core/src/test/java/org/briarproject/bramble/rendezvous/RendezvousPollerImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/rendezvous/RendezvousPollerImplTest.java index eddaacce4..42f522e4c 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/rendezvous/RendezvousPollerImplTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/rendezvous/RendezvousPollerImplTest.java @@ -17,8 +17,8 @@ import org.briarproject.bramble.api.plugin.ConnectionHandler; import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; -import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent; -import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent; +import org.briarproject.bramble.api.plugin.event.TransportActiveEvent; +import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent; import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.rendezvous.KeyMaterialSource; import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint; @@ -178,10 +178,10 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase { rendezvousPoller.startService(); context.assertIsSatisfied(); - // Enable the transport - no endpoints should be created yet + // Activate the transport - no endpoints should be created yet expectGetPlugin(); - rendezvousPoller.eventOccurred(new TransportEnabledEvent(transportId)); + rendezvousPoller.eventOccurred(new TransportActiveEvent(transportId)); context.assertIsSatisfied(); // Add the pending contact - endpoint should be created and polled @@ -212,8 +212,8 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase { new PendingContactRemovedEvent(pendingContact.getId())); context.assertIsSatisfied(); - // Disable the transport - endpoint is already closed - rendezvousPoller.eventOccurred(new TransportDisabledEvent(transportId)); + // Deactivate the transport - endpoint is already closed + rendezvousPoller.eventOccurred(new TransportInactiveEvent(transportId)); } @Test @@ -230,10 +230,10 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase { rendezvousPoller.startService(); context.assertIsSatisfied(); - // Enable the transport - no endpoints should be created yet + // Activate the transport - no endpoints should be created yet expectGetPlugin(); - rendezvousPoller.eventOccurred(new TransportEnabledEvent(transportId)); + rendezvousPoller.eventOccurred(new TransportActiveEvent(transportId)); context.assertIsSatisfied(); // Add the pending contact - endpoint should be created and polled @@ -269,12 +269,12 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase { new PendingContactRemovedEvent(pendingContact.getId())); context.assertIsSatisfied(); - // Disable the transport - endpoint is already closed - rendezvousPoller.eventOccurred(new TransportDisabledEvent(transportId)); + // Deactivate the transport - endpoint is already closed + rendezvousPoller.eventOccurred(new TransportInactiveEvent(transportId)); } @Test - public void testCreatesAndClosesEndpointsWhenTransportIsEnabledAndDisabled() + public void testCreatesAndClosesEndpointsWhenTransportIsActivatedAndDeactivated() throws Exception { long beforeExpiry = pendingContact.getTimestamp(); @@ -292,19 +292,19 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase { new PendingContactAddedEvent(pendingContact)); context.assertIsSatisfied(); - // Enable the transport - endpoint should be created + // Activate the transport - endpoint should be created expectGetPlugin(); expectCreateEndpoint(); expectStateChangedEvent(WAITING_FOR_CONNECTION); - rendezvousPoller.eventOccurred(new TransportEnabledEvent(transportId)); + rendezvousPoller.eventOccurred(new TransportActiveEvent(transportId)); context.assertIsSatisfied(); - // Disable the transport - endpoint should be closed + // Deactivate the transport - endpoint should be closed expectCloseEndpoint(); expectStateChangedEvent(OFFLINE); - rendezvousPoller.eventOccurred(new TransportDisabledEvent(transportId)); + rendezvousPoller.eventOccurred(new TransportInactiveEvent(transportId)); context.assertIsSatisfied(); // Remove the pending contact - endpoint is already closed diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java index a927fe409..b3488804e 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java @@ -14,8 +14,8 @@ import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.TransportId; -import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent; -import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent; +import org.briarproject.bramble.api.plugin.event.TransportActiveEvent; +import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent; import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.SettingsManager; import org.briarproject.briar.android.controller.DbControllerImpl; @@ -86,16 +86,16 @@ public class NavDrawerControllerImpl extends DbControllerImpl @Override public void eventOccurred(Event e) { - if (e instanceof TransportEnabledEvent) { - TransportId id = ((TransportEnabledEvent) e).getTransportId(); + if (e instanceof TransportActiveEvent) { + TransportId id = ((TransportActiveEvent) e).getTransportId(); if (LOG.isLoggable(INFO)) { - LOG.info("TransportEnabledEvent: " + id.getString()); + LOG.info("TransportActiveEvent: " + id.getString()); } listener.stateUpdate(id, true); - } else if (e instanceof TransportDisabledEvent) { - TransportId id = ((TransportDisabledEvent) e).getTransportId(); + } else if (e instanceof TransportInactiveEvent) { + TransportId id = ((TransportInactiveEvent) e).getTransportId(); if (LOG.isLoggable(INFO)) { - LOG.info("TransportDisabledEvent: " + id.getString()); + LOG.info("TransportInactiveEvent: " + id.getString()); } listener.stateUpdate(id, false); } diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java index 04350c8a6..b7dfa5783 100644 --- a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java @@ -24,7 +24,7 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.TorConstants; import org.briarproject.bramble.api.plugin.TransportId; -import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent; +import org.briarproject.bramble.api.plugin.event.TransportActiveEvent; import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.system.Clock; @@ -120,8 +120,8 @@ class FeedManagerImpl implements FeedManager, EventListener, OpenDatabaseHook, @Override public void eventOccurred(Event e) { - if (e instanceof TransportEnabledEvent) { - TransportId t = ((TransportEnabledEvent) e).getTransportId(); + if (e instanceof TransportActiveEvent) { + TransportId t = ((TransportActiveEvent) e).getTransportId(); if (t.equals(TorConstants.ID)) { startFeedExecutor(); } From 242d6f8a0eec9e63c2c5c2a223e26256bc4f24ed Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 16 Jan 2020 12:38:03 +0000 Subject: [PATCH 11/69] Move to enabling state earlier in Tor startup. --- .../bramble/plugin/tor/AndroidTorPlugin.java | 2 +- .../bramble/plugin/tor/TorPlugin.java | 23 ++++++++++++------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPlugin.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPlugin.java index 715c80a83..b48721aee 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPlugin.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPlugin.java @@ -75,7 +75,7 @@ class AndroidTorPlugin extends TorPlugin { @Override protected void enableNetwork(boolean enable) throws IOException { - if (!state.isRunning()) return; + if (!state.isTorRunning()) return; if (enable) wakeLock.acquire(); super.enableNetwork(enable); if (!enable) wakeLock.release(); diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index a32eb7b2c..2ff626831 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -193,6 +193,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @Override public void start() throws PluginException { if (used.getAndSet(true)) throw new IllegalStateException(); + state.setStarted(); + callback.pluginStateChanged(getState()); if (!torDirectory.exists()) { if (!torDirectory.mkdirs()) { LOG.warning("Could not create Tor directory."); @@ -280,8 +282,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } catch (IOException e) { throw new PluginException(e); } - state.setStarted(); - callback.pluginStateChanged(getState()); + state.setTorStarted(); // Check whether we're online updateConnectionStatus(networkManager.getNetworkStatus(), batteryManager.isCharging()); @@ -423,7 +424,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } private void publishHiddenService(String port) { - if (!state.isRunning()) return; + if (!state.isTorRunning()) return; LOG.info("Creating hidden service"); String privKey = settings.get(HS_PRIVKEY); Map portLines = singletonMap(80, "127.0.0.1:" + port); @@ -479,7 +480,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } protected void enableNetwork(boolean enable) throws IOException { - if (!state.isRunning()) return; + if (!state.isTorRunning()) return; state.enableNetwork(enable); callback.pluginStateChanged(getState()); controlConnection.setConf("DisableNetwork", enable ? "0" : "1"); @@ -763,7 +764,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private void updateConnectionStatus(NetworkStatus status, boolean charging) { connectionStatusExecutor.execute(() -> { - if (!state.isRunning()) return; + if (!state.isTorRunning()) return; NetworkConfig config = getNetworkConfig(status, charging); state.setDisabledBySettings(config.disabledBySettings, config.reasonDisabled); @@ -856,7 +857,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } private void enableConnectionPadding(boolean enable) throws IOException { - if (!state.isRunning()) return; + if (!state.isTorRunning()) return; controlConnection.setConf("ConnectionPadding", enable ? "1" : "0"); } @@ -887,6 +888,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @GuardedBy("this") private boolean started = false, stopped = false, + torStarted = false, networkInitialised = false, networkEnabled = false, bootstrapped = false, @@ -904,8 +906,13 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { started = true; } - synchronized boolean isRunning() { - return started && !stopped; + synchronized void setTorStarted() { + torStarted = true; + } + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + synchronized boolean isTorRunning() { + return torStarted && !stopped; } @Nullable From 7561c5039edc339c9d03a063dfb982c053b6dbb0 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 16 Jan 2020 13:01:41 +0000 Subject: [PATCH 12/69] Reset backoff before notifying of new state. The new state may cause the poller to poll the plugin. Let's avoid a race between updating and querying the polling interval. --- .../bramble/plugin/bluetooth/BluetoothPlugin.java | 2 +- .../org/briarproject/bramble/plugin/tcp/TcpPlugin.java | 2 +- .../org/briarproject/bramble/plugin/tor/TorPlugin.java | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java index f4e8bb128..fd2966b13 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java @@ -237,7 +237,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { try { conn = acceptConnection(ss); } catch (IOException e) { - // This is expected when the socket is closed + // This is expected when the server socket is closed LOG.info("Server socket closed"); state.clearServerSocket(); return; diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java index e92314ee5..63d059f83 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java @@ -155,9 +155,9 @@ abstract class TcpPlugin implements DuplexPlugin { InetSocketAddress local = (InetSocketAddress) ss.getLocalSocketAddress(); setLocalSocketAddress(local); - callback.pluginStateChanged(getState()); if (LOG.isLoggable(INFO)) LOG.info("Listening on " + scrubSocketAddress(local)); + callback.pluginStateChanged(getState()); acceptContactConnections(ss); }); } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index 2ff626831..dc7e495e8 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -652,8 +652,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { new TorTransportConnection(this, s)); } } catch (IOException e) { - // This is expected when the socket is closed - if (LOG.isLoggable(INFO)) LOG.info(e.toString()); + // This is expected when the server socket is closed + LOG.info("Rendezvous server socket closed"); } }); Map portLines = @@ -682,9 +682,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { public void circuitStatus(String status, String id, String path) { if (status.equals("BUILT") && state.getAndSetCircuitBuilt()) { - callback.pluginStateChanged(getState()); LOG.info("First circuit built"); backoff.reset(); + callback.pluginStateChanged(getState()); } } @@ -716,8 +716,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg); if (severity.equals("NOTICE") && msg.startsWith("Bootstrapped 100%")) { state.setBootstrapped(); - callback.pluginStateChanged(getState()); backoff.reset(); + callback.pluginStateChanged(getState()); } } From 465ba3d3371e955e9efba717caf617191e096491 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 16 Jan 2020 13:08:16 +0000 Subject: [PATCH 13/69] Remove unnecessary inner class, state checks. --- .../bramble/plugin/tor/AndroidTorPlugin.java | 1 - .../bramble/plugin/tor/TorPlugin.java | 172 +++++++----------- 2 files changed, 69 insertions(+), 104 deletions(-) diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPlugin.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPlugin.java index b48721aee..68dc17855 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPlugin.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPlugin.java @@ -75,7 +75,6 @@ class AndroidTorPlugin extends TorPlugin { @Override protected void enableNetwork(boolean enable) throws IOException { - if (!state.isTorRunning()) return; if (enable) wakeLock.acquire(); super.enableNetwork(enable); if (!enable) wakeLock.release(); diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index dc7e495e8..7010be7df 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -57,7 +57,6 @@ import java.util.zip.ZipInputStream; import javax.annotation.Nullable; import javax.annotation.concurrent.GuardedBy; -import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.ThreadSafe; import javax.net.SocketFactory; @@ -480,7 +479,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } protected void enableNetwork(boolean enable) throws IOException { - if (!state.isTorRunning()) return; state.enableNetwork(enable); callback.pluginStateChanged(getState()); controlConnection.setConf("DisableNetwork", enable ? "0" : "1"); @@ -713,6 +711,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @Override public void message(String severity, String msg) { + if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg); if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg); if (severity.equals("NOTICE") && msg.startsWith("Bootstrapped 100%")) { state.setBootstrapped(); @@ -754,7 +753,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private void disableNetwork() { connectionStatusExecutor.execute(() -> { try { - enableNetwork(false); + if (state.isTorRunning()) enableNetwork(false); } catch (IOException ex) { logException(LOG, WARNING, ex); } @@ -765,91 +764,79 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { boolean charging) { connectionStatusExecutor.execute(() -> { if (!state.isTorRunning()) return; - NetworkConfig config = getNetworkConfig(status, charging); - state.setDisabledBySettings(config.disabledBySettings, - config.reasonDisabled); - callback.pluginStateChanged(getState()); - applyNetworkConfig(config); - }); - } + boolean online = status.isConnected(); + boolean wifi = status.isWifi(); + String country = locationUtils.getCurrentCountry(); + boolean blocked = + circumventionProvider.isTorProbablyBlocked(country); + int network = settings.getInt(PREF_TOR_NETWORK, + PREF_TOR_NETWORK_AUTOMATIC); + boolean useMobile = settings.getBoolean(PREF_TOR_MOBILE, true); + boolean onlyWhenCharging = + settings.getBoolean(PREF_TOR_ONLY_WHEN_CHARGING, false); + boolean bridgesWork = circumventionProvider.doBridgesWork(country); + boolean automatic = network == PREF_TOR_NETWORK_AUTOMATIC; - private NetworkConfig getNetworkConfig(NetworkStatus status, - boolean charging) { - boolean online = status.isConnected(); - boolean wifi = status.isWifi(); - String country = locationUtils.getCurrentCountry(); - boolean blocked = circumventionProvider.isTorProbablyBlocked(country); - int network = - settings.getInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_AUTOMATIC); - boolean useMobile = settings.getBoolean(PREF_TOR_MOBILE, true); - boolean onlyWhenCharging = - settings.getBoolean(PREF_TOR_ONLY_WHEN_CHARGING, false); - boolean bridgesWork = circumventionProvider.doBridgesWork(country); - boolean automatic = network == PREF_TOR_NETWORK_AUTOMATIC; - - if (LOG.isLoggable(INFO)) { - LOG.info("Online: " + online + ", wifi: " + wifi); - if (country.isEmpty()) LOG.info("Country code unknown"); - else LOG.info("Country code: " + country); - LOG.info("Charging: " + charging); - } - - boolean enableNetwork = false, enableBridges = false; - boolean useMeek = false, enableConnectionPadding = false; - boolean disabledBySettings = false; - int reasonDisabled = REASON_STARTING_STOPPING; - - if (!online) { - LOG.info("Disabling network, device is offline"); - } else if (network == PREF_TOR_NETWORK_NEVER) { - LOG.info("Disabling network, user has disabled Tor"); - disabledBySettings = true; - reasonDisabled = REASON_USER; - } else if (!charging && onlyWhenCharging) { - LOG.info("Disabling network, device is on battery"); - disabledBySettings = true; - reasonDisabled = REASON_BATTERY; - } else if (!useMobile && !wifi) { - LOG.info("Disabling network, device is using mobile data"); - disabledBySettings = true; - reasonDisabled = REASON_MOBILE_DATA; - } else if (automatic && blocked && !bridgesWork) { - LOG.info("Disabling network, country is blocked"); - disabledBySettings = true; - reasonDisabled = REASON_COUNTRY_BLOCKED; - } else if (network == PREF_TOR_NETWORK_WITH_BRIDGES || - (automatic && bridgesWork)) { - if (circumventionProvider.needsMeek(country)) { - LOG.info("Enabling network, using meek bridges"); - enableBridges = true; - useMeek = true; - } else { - LOG.info("Enabling network, using obfs4 bridges"); - enableBridges = true; + if (LOG.isLoggable(INFO)) { + LOG.info("Online: " + online + ", wifi: " + wifi); + if (country.isEmpty()) LOG.info("Country code unknown"); + else LOG.info("Country code: " + country); + LOG.info("Charging: " + charging); } - enableNetwork = true; - } else { - LOG.info("Enabling network, not using bridges"); - enableNetwork = true; - } - if (online && wifi && charging) { - LOG.info("Enabling connection padding"); - enableConnectionPadding = true; - } else { - LOG.info("Disabling connection padding"); - } + boolean enableNetwork = false, enableBridges = false; + boolean useMeek = false, enableConnectionPadding = false; + boolean disabledBySettings = false; + int reasonDisabled = REASON_STARTING_STOPPING; - return new NetworkConfig(enableNetwork, enableBridges, useMeek, - enableConnectionPadding, disabledBySettings, reasonDisabled); - } + if (!online) { + LOG.info("Disabling network, device is offline"); + } else if (network == PREF_TOR_NETWORK_NEVER) { + LOG.info("Disabling network, user has disabled Tor"); + disabledBySettings = true; + reasonDisabled = REASON_USER; + } else if (!charging && onlyWhenCharging) { + LOG.info("Disabling network, device is on battery"); + disabledBySettings = true; + reasonDisabled = REASON_BATTERY; + } else if (!useMobile && !wifi) { + LOG.info("Disabling network, device is using mobile data"); + disabledBySettings = true; + reasonDisabled = REASON_MOBILE_DATA; + } else if (automatic && blocked && !bridgesWork) { + LOG.info("Disabling network, country is blocked"); + disabledBySettings = true; + reasonDisabled = REASON_COUNTRY_BLOCKED; + } else if (network == PREF_TOR_NETWORK_WITH_BRIDGES || + (automatic && bridgesWork)) { + if (circumventionProvider.needsMeek(country)) { + LOG.info("Enabling network, using meek bridges"); + enableBridges = true; + useMeek = true; + } else { + LOG.info("Enabling network, using obfs4 bridges"); + enableBridges = true; + } + enableNetwork = true; + } else { + LOG.info("Enabling network, not using bridges"); + enableNetwork = true; + } + + if (online && wifi && charging) { + LOG.info("Enabling connection padding"); + enableConnectionPadding = true; + } else { + LOG.info("Disabling connection padding"); + } + + state.setDisabledBySettings(disabledBySettings, reasonDisabled); + callback.pluginStateChanged(getState()); - private void applyNetworkConfig(NetworkConfig config) { - connectionStatusExecutor.execute(() -> { try { - enableBridges(config.enableBridges, config.useMeek); - enableNetwork(config.enableNetwork); - enableConnectionPadding(config.enableConnectionPadding); + enableBridges(enableBridges, useMeek); + enableNetwork(enableNetwork); + enableConnectionPadding(enableConnectionPadding); } catch (IOException e) { logException(LOG, WARNING, e); } @@ -857,30 +844,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } private void enableConnectionPadding(boolean enable) throws IOException { - if (!state.isTorRunning()) return; controlConnection.setConf("ConnectionPadding", enable ? "1" : "0"); } - @Immutable - @NotNullByDefault - private static class NetworkConfig { - - private final boolean enableNetwork, enableBridges, useMeek; - private final boolean enableConnectionPadding, disabledBySettings; - private final int reasonDisabled; - - private NetworkConfig(boolean enableNetwork, boolean enableBridges, - boolean useMeek, boolean enableConnectionPadding, - boolean disabledBySettings, int reasonDisabled) { - this.enableNetwork = enableNetwork; - this.enableBridges = enableBridges; - this.useMeek = useMeek; - this.enableConnectionPadding = enableConnectionPadding; - this.disabledBySettings = disabledBySettings; - this.reasonDisabled = reasonDisabled; - } - } - @ThreadSafe @NotNullByDefault protected static class PluginState { From dd1509350c304deb515cd7db1acdae98b56aef8c Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 17 Jan 2020 12:38:03 +0000 Subject: [PATCH 14/69] Close server socket when BT is disabled. --- .../briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java index fd2966b13..e803123c9 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java @@ -138,6 +138,8 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { void onAdapterDisabled() { LOG.info("Bluetooth disabled"); connectionLimiter.allConnectionsClosed(); + // The server socket may not have been closed automatically + tryToClose(state.clearServerSocket()); callback.pluginStateChanged(getState()); } From 5e60a717fc752b238a41f3bbe39eed918f3d2a53 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 17 Jan 2020 12:38:43 +0000 Subject: [PATCH 15/69] Remove debug logging. --- .../org/briarproject/bramble/plugin/PluginManagerImpl.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java index 36a7d8252..064b12856 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java @@ -314,10 +314,6 @@ class PluginManagerImpl implements PluginManager, Service { } else if (oldState == ACTIVE) { eventBus.broadcast(new TransportInactiveEvent(id)); } - } else { - // TODO: Remove - if (LOG.isLoggable(INFO)) - LOG.info(id + " stayed in state " + oldState); } } From 8bb534564fd4f6e5894d28d41ca71f871aff78a1 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 20 Jan 2020 14:03:12 +0000 Subject: [PATCH 16/69] Remove redundant logging. --- .../main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java | 1 - 1 file changed, 1 deletion(-) diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index 7010be7df..c4f063dc4 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -711,7 +711,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @Override public void message(String severity, String msg) { - if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg); if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg); if (severity.equals("NOTICE") && msg.startsWith("Bootstrapped 100%")) { state.setBootstrapped(); From c08bdf96cdcbdc6655477dbf8cbf565eeb3dc92f Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 20 Jan 2020 14:48:33 +0000 Subject: [PATCH 17/69] Update javadocs for lock-safe methods. --- .../briarproject/bramble/api/event/EventBus.java | 2 ++ .../bramble/api/plugin/PluginCallback.java | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/event/EventBus.java b/bramble-api/src/main/java/org/briarproject/bramble/api/event/EventBus.java index d27449833..cf57b67ed 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/event/EventBus.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/event/EventBus.java @@ -18,6 +18,8 @@ public interface EventBus { /** * Asynchronously notifies all listeners of an event. Listeners are * notified on the {@link EventExecutor}. + *

+ * This method can safely be called while holding a lock. */ void broadcast(Event e); } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/PluginCallback.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/PluginCallback.java index ed057a958..1b149cde1 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/PluginCallback.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/PluginCallback.java @@ -2,6 +2,9 @@ package org.briarproject.bramble.api.plugin; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.Plugin.State; +import org.briarproject.bramble.api.plugin.event.TransportActiveEvent; +import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent; +import org.briarproject.bramble.api.plugin.event.TransportStateEvent; import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.settings.Settings; @@ -33,7 +36,17 @@ public interface PluginCallback extends ConnectionHandler { void mergeLocalProperties(TransportProperties p); /** - * Signals that the transport's state may have changed. + * Informs the callback of the plugin's current state. + *

+ * If the current state is different from the previous state, the callback + * will broadcast a {@link TransportStateEvent}. If the current state is + * {@link State#ACTIVE} and the previous state was not + * {@link State#ACTIVE}, the callback will broadcast a + * {@link TransportActiveEvent}. If the current state is not + * {@link State#ACTIVE} and the previous state was {@link State#ACTIVE}, + * the callback will broadcast a {@link TransportInactiveEvent}. + *

+ * This method can safely be called while holding a lock. */ void pluginStateChanged(State state); } From 4130662e1f91cb94f3046f39bd7ce2435a3166b6 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 20 Jan 2020 15:00:16 +0000 Subject: [PATCH 18/69] Notify callback of state changes while holding lock. --- .../bramble/plugin/tcp/AndroidLanTcpPlugin.java | 1 - .../bramble/plugin/bluetooth/BluetoothPlugin.java | 6 +++--- .../briarproject/bramble/plugin/tcp/TcpPlugin.java | 10 +++++----- .../briarproject/bramble/plugin/tor/TorPlugin.java | 14 +++++++------- .../bramble/plugin/modem/ModemPlugin.java | 11 +++++------ 5 files changed, 20 insertions(+), 22 deletions(-) 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 e9ef6fe41..f269d3406 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 @@ -73,7 +73,6 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener { if (used.getAndSet(true)) throw new IllegalStateException(); initialisePortProperty(); state.setStarted(); - callback.pluginStateChanged(getState()); updateConnectionStatus(); } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java index e803123c9..4de8cc97e 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java @@ -168,7 +168,6 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { } updateProperties(); state.setStarted(); - callback.pluginStateChanged(getState()); loadSettings(callback.getSettings()); if (shouldAllowContactConnections()) { if (isAdapterEnabled()) bind(); @@ -203,7 +202,6 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { return; } backoff.reset(); - callback.pluginStateChanged(getState()); acceptContactConnections(ss); }); } @@ -254,7 +252,6 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { @Override public void stop() { SS ss = state.setStopped(); - callback.pluginStateChanged(getState()); tryToClose(ss); disableAdapterIfEnabledByUs(); } @@ -491,6 +488,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { synchronized void setStarted() { started = true; + callback.pluginStateChanged(getState()); } @Nullable @@ -498,12 +496,14 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { stopped = true; SS ss = serverSocket; serverSocket = null; + callback.pluginStateChanged(getState()); return ss; } synchronized boolean setServerSocket(SS ss) { if (stopped || serverSocket != null) return false; serverSocket = ss; + callback.pluginStateChanged(getState()); return true; } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java index 63d059f83..35f291633 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java @@ -123,7 +123,6 @@ abstract class TcpPlugin implements DuplexPlugin { public void start() { if (used.getAndSet(true)) throw new IllegalStateException(); state.setStarted(); - callback.pluginStateChanged(getState()); bind(); } @@ -157,7 +156,6 @@ abstract class TcpPlugin implements DuplexPlugin { setLocalSocketAddress(local); if (LOG.isLoggable(INFO)) LOG.info("Listening on " + scrubSocketAddress(local)); - callback.pluginStateChanged(getState()); acceptContactConnections(ss); }); } @@ -179,7 +177,6 @@ abstract class TcpPlugin implements DuplexPlugin { // This is expected when the server socket is closed LOG.info("Server socket closed"); state.clearServerSocket(ss); - callback.pluginStateChanged(getState()); return; } if (LOG.isLoggable(INFO)) @@ -193,7 +190,6 @@ abstract class TcpPlugin implements DuplexPlugin { @Override public void stop() { ServerSocket ss = state.setStopped(); - callback.pluginStateChanged(getState()); tryToClose(ss, LOG, WARNING); } @@ -376,7 +372,7 @@ abstract class TcpPlugin implements DuplexPlugin { @ThreadSafe @NotNullByDefault - protected static class PluginState { + protected class PluginState { @GuardedBy("this") private boolean started = false, stopped = false; @@ -386,6 +382,7 @@ abstract class TcpPlugin implements DuplexPlugin { synchronized void setStarted() { started = true; + callback.pluginStateChanged(getState()); } @Nullable @@ -393,6 +390,7 @@ abstract class TcpPlugin implements DuplexPlugin { stopped = true; ServerSocket ss = serverSocket; serverSocket = null; + callback.pluginStateChanged(getState()); return ss; } @@ -404,11 +402,13 @@ abstract class TcpPlugin implements DuplexPlugin { synchronized boolean setServerSocket(ServerSocket ss) { if (stopped || serverSocket != null) return false; serverSocket = ss; + callback.pluginStateChanged(getState()); return true; } synchronized void clearServerSocket(ServerSocket ss) { if (serverSocket == ss) serverSocket = null; + callback.pluginStateChanged(getState()); } synchronized State getState() { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index c4f063dc4..f284999dd 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -193,7 +193,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { public void start() throws PluginException { if (used.getAndSet(true)) throw new IllegalStateException(); state.setStarted(); - callback.pluginStateChanged(getState()); if (!torDirectory.exists()) { if (!torDirectory.mkdirs()) { LOG.warning("Could not create Tor directory."); @@ -480,7 +479,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { protected void enableNetwork(boolean enable) throws IOException { state.enableNetwork(enable); - callback.pluginStateChanged(getState()); controlConnection.setConf("DisableNetwork", enable ? "0" : "1"); } @@ -506,7 +504,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @Override public void stop() { ServerSocket ss = state.setStopped(); - callback.pluginStateChanged(getState()); tryToClose(ss, LOG, WARNING); if (controlSocket != null && controlConnection != null) { try { @@ -682,7 +679,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { state.getAndSetCircuitBuilt()) { LOG.info("First circuit built"); backoff.reset(); - callback.pluginStateChanged(getState()); } } @@ -715,7 +711,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { if (severity.equals("NOTICE") && msg.startsWith("Bootstrapped 100%")) { state.setBootstrapped(); backoff.reset(); - callback.pluginStateChanged(getState()); } } @@ -830,7 +825,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } state.setDisabledBySettings(disabledBySettings, reasonDisabled); - callback.pluginStateChanged(getState()); try { enableBridges(enableBridges, useMeek); @@ -848,7 +842,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @ThreadSafe @NotNullByDefault - protected static class PluginState { + protected class PluginState { @GuardedBy("this") private boolean started = false, @@ -869,6 +863,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { synchronized void setStarted() { started = true; + callback.pluginStateChanged(getState()); } synchronized void setTorStarted() { @@ -885,16 +880,19 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { stopped = true; ServerSocket ss = serverSocket; serverSocket = null; + callback.pluginStateChanged(getState()); return ss; } synchronized void setBootstrapped() { bootstrapped = true; + callback.pluginStateChanged(getState()); } synchronized boolean getAndSetCircuitBuilt() { boolean firstCircuit = !circuitBuilt; circuitBuilt = true; + callback.pluginStateChanged(getState()); return firstCircuit; } @@ -902,12 +900,14 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { networkInitialised = true; networkEnabled = enable; if (!enable) circuitBuilt = false; + callback.pluginStateChanged(getState()); } synchronized void setDisabledBySettings(boolean disabledBySettings, int reasonDisabled) { this.disabledBySettings = disabledBySettings; this.reasonDisabled = reasonDisabled; + callback.pluginStateChanged(getState()); } synchronized boolean setServerSocket(ServerSocket ss) { diff --git a/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java b/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java index 89a11ff2f..e2eb69ca8 100644 --- a/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java +++ b/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java @@ -84,7 +84,6 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { public void start() throws PluginException { if (used.getAndSet(true)) throw new IllegalStateException(); state.setStarted(); - callback.pluginStateChanged(getState()); for (String portName : serialPortList.getPortNames()) { if (LOG.isLoggable(INFO)) LOG.info("Trying to initialise modem on " + portName); @@ -94,7 +93,6 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { if (LOG.isLoggable(INFO)) LOG.info("Initialised modem on " + portName); state.setInitialised(); - callback.pluginStateChanged(getState()); return; } catch (IOException e) { logException(LOG, WARNING, e); @@ -102,14 +100,12 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { } LOG.warning("Failed to initialised modem"); state.setFailed(); - callback.pluginStateChanged(getState()); throw new PluginException(); } @Override public void stop() { state.setStopped(); - callback.pluginStateChanged(getState()); if (modem != null) { try { modem.stop(); @@ -162,7 +158,6 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { } LOG.warning("Failed to initialise modem"); state.setFailed(); - callback.pluginStateChanged(getState()); } @Override @@ -256,7 +251,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { @ThreadSafe @NotNullByDefault - private static class PluginState { + private class PluginState { @GuardedBy("this") private boolean started = false, @@ -266,18 +261,22 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { private synchronized void setStarted() { started = true; + callback.pluginStateChanged(getState()); } private synchronized void setStopped() { stopped = true; + callback.pluginStateChanged(getState()); } private synchronized void setInitialised() { initialised = true; + callback.pluginStateChanged(getState()); } private synchronized void setFailed() { failed = true; + callback.pluginStateChanged(getState()); } private State getState() { From 297dbe0b16a8ac5ad5f01a62f94d225319e002d3 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 20 Jan 2020 16:20:36 +0000 Subject: [PATCH 19/69] Only update bridge and padding settings if network is enabled. --- .../bramble/plugin/tor/TorPlugin.java | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index f284999dd..d20901002 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -801,35 +801,39 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { LOG.info("Disabling network, country is blocked"); disabledBySettings = true; reasonDisabled = REASON_COUNTRY_BLOCKED; - } else if (network == PREF_TOR_NETWORK_WITH_BRIDGES || - (automatic && bridgesWork)) { - if (circumventionProvider.needsMeek(country)) { - LOG.info("Enabling network, using meek bridges"); - enableBridges = true; - useMeek = true; - } else { - LOG.info("Enabling network, using obfs4 bridges"); - enableBridges = true; - } - enableNetwork = true; } else { - LOG.info("Enabling network, not using bridges"); + LOG.info("Enabling network"); enableNetwork = true; + if (network == PREF_TOR_NETWORK_WITH_BRIDGES || + (automatic && bridgesWork)) { + if (circumventionProvider.needsMeek(country)) { + LOG.info("Using meek bridges"); + enableBridges = true; + useMeek = true; + } else { + LOG.info("Using obfs4 bridges"); + enableBridges = true; + } + } else { + LOG.info("Not using bridges"); + } + if (wifi && charging) { + LOG.info("Enabling connection padding"); + enableConnectionPadding = true; + } else { + LOG.info("Disabling connection padding"); + } } - if (online && wifi && charging) { - LOG.info("Enabling connection padding"); - enableConnectionPadding = true; - } else { - LOG.info("Disabling connection padding"); - } state.setDisabledBySettings(disabledBySettings, reasonDisabled); try { - enableBridges(enableBridges, useMeek); + if (enableNetwork) { + enableBridges(enableBridges, useMeek); + enableConnectionPadding(enableConnectionPadding); + } enableNetwork(enableNetwork); - enableConnectionPadding(enableConnectionPadding); } catch (IOException e) { logException(LOG, WARNING, e); } From c42a98792727b40c51ca468f46f1bcaa5344dbf9 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 16 Jan 2020 12:33:36 +0000 Subject: [PATCH 20/69] Use amber icon when enabling transports. --- .../android/navdrawer/NavDrawerActivity.java | 44 ++++++++++--------- .../navdrawer/NavDrawerController.java | 3 +- .../navdrawer/NavDrawerControllerImpl.java | 26 +++++------ .../navdrawer/TransportStateListener.java | 3 +- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java index 1c71573c8..3c63d1c4e 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java @@ -23,6 +23,7 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.plugin.BluetoothConstants; import org.briarproject.bramble.api.plugin.LanTcpConstants; +import org.briarproject.bramble.api.plugin.Plugin.State; import org.briarproject.bramble.api.plugin.TorConstants; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.briar.R; @@ -64,6 +65,8 @@ import static androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE; import static java.util.Objects.requireNonNull; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING; +import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; +import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING; import static org.briarproject.briar.android.BriarService.EXTRA_STARTUP_FAILED; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD; import static org.briarproject.briar.android.navdrawer.IntentRouter.handleExternalIntent; @@ -375,21 +378,21 @@ public class NavDrawerActivity extends BriarActivity implements Transport tor = new Transport(); tor.id = TorConstants.ID; - tor.enabled = controller.isTransportRunning(tor.id); + tor.state = controller.getTransportState(tor.id); tor.iconId = R.drawable.transport_tor; tor.textId = R.string.transport_tor; transports.add(tor); Transport bt = new Transport(); bt.id = BluetoothConstants.ID; - bt.enabled = controller.isTransportRunning(bt.id); + bt.state = controller.getTransportState(bt.id); bt.iconId = R.drawable.transport_bt; bt.textId = R.string.transport_bt; transports.add(bt); Transport lan = new Transport(); lan.id = LanTcpConstants.ID; - lan.enabled = controller.isTransportRunning(lan.id); + lan.state = controller.getTransportState(lan.id); lan.iconId = R.drawable.transport_lan; lan.textId = R.string.transport_lan; transports.add(lan); @@ -423,9 +426,12 @@ public class NavDrawerActivity extends BriarActivity implements Transport t = getItem(position); int c; - if (t.enabled) { + if (t.state == ACTIVE) { c = ContextCompat.getColor(NavDrawerActivity.this, R.color.briar_green_light); + } else if (t.state == ENABLING) { + c = ContextCompat.getColor(NavDrawerActivity.this, + R.color.briar_yellow); } else { c = ContextCompat.getColor(NavDrawerActivity.this, android.R.color.tertiary_text_light); @@ -444,35 +450,31 @@ public class NavDrawerActivity extends BriarActivity implements }; } - @UiThread - private void setTransport(TransportId id, boolean enabled) { + private void updateTransports() { + if (transports == null || transportsAdapter == null) return; + for (Transport t : transports) { + t.state = controller.getTransportState(t.id); + } + transportsAdapter.notifyDataSetChanged(); + } + + @Override + public void stateUpdate(TransportId id, State state) { if (transports == null || transportsAdapter == null) return; for (Transport t : transports) { if (t.id.equals(id)) { - t.enabled = enabled; + t.state = state; transportsAdapter.notifyDataSetChanged(); break; } } } - private void updateTransports() { - if (transports == null || transportsAdapter == null) return; - for (Transport t : transports) { - t.enabled = controller.isTransportRunning(t.id); - } - transportsAdapter.notifyDataSetChanged(); - } - - @Override - public void stateUpdate(TransportId id, boolean enabled) { - setTransport(id, enabled); - } - + @UiThread private static class Transport { private TransportId id; - private boolean enabled; + private State state; private int iconId; private int textId; } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerController.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerController.java index 44c8c8dfd..5b9c3a963 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerController.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerController.java @@ -3,6 +3,7 @@ package org.briarproject.briar.android.navdrawer; import android.content.Context; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.Plugin.State; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.briar.android.controller.ActivityLifecycleController; import org.briarproject.briar.android.controller.handler.ResultHandler; @@ -10,7 +11,7 @@ import org.briarproject.briar.android.controller.handler.ResultHandler; @NotNullByDefault public interface NavDrawerController extends ActivityLifecycleController { - boolean isTransportRunning(TransportId transportId); + State getTransportState(TransportId transportId); void showExpiryWarning(ResultHandler handler); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java index b3488804e..cbf15708a 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java @@ -12,10 +12,10 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.plugin.Plugin; +import org.briarproject.bramble.api.plugin.Plugin.State; import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.TransportId; -import org.briarproject.bramble.api.plugin.event.TransportActiveEvent; -import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent; +import org.briarproject.bramble.api.plugin.event.TransportStateEvent; import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.SettingsManager; import org.briarproject.briar.android.controller.DbControllerImpl; @@ -30,7 +30,7 @@ import static java.util.concurrent.TimeUnit.DAYS; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; -import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; +import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE; import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD; @@ -86,18 +86,14 @@ public class NavDrawerControllerImpl extends DbControllerImpl @Override public void eventOccurred(Event e) { - if (e instanceof TransportActiveEvent) { - TransportId id = ((TransportActiveEvent) e).getTransportId(); + if (e instanceof TransportStateEvent) { + TransportStateEvent t = (TransportStateEvent) e; + TransportId id = t.getTransportId(); + State state = t.getState(); if (LOG.isLoggable(INFO)) { - LOG.info("TransportActiveEvent: " + id.getString()); + LOG.info("TransportStateEvent: " + id + " is " + state); } - listener.stateUpdate(id, true); - } else if (e instanceof TransportInactiveEvent) { - TransportId id = ((TransportInactiveEvent) e).getTransportId(); - if (LOG.isLoggable(INFO)) { - LOG.info("TransportInactiveEvent: " + id.getString()); - } - listener.stateUpdate(id, false); + listener.stateUpdate(id, state); } } @@ -175,9 +171,9 @@ public class NavDrawerControllerImpl extends DbControllerImpl } @Override - public boolean isTransportRunning(TransportId transportId) { + public State getTransportState(TransportId transportId) { Plugin plugin = pluginManager.getPlugin(transportId); - return plugin != null && plugin.getState() == ACTIVE; + return plugin == null ? DISABLED : plugin.getState(); } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/TransportStateListener.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/TransportStateListener.java index f47e311e3..54458dcd7 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/TransportStateListener.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/TransportStateListener.java @@ -1,5 +1,6 @@ package org.briarproject.briar.android.navdrawer; +import org.briarproject.bramble.api.plugin.Plugin.State; import org.briarproject.bramble.api.plugin.TransportId; import androidx.annotation.UiThread; @@ -7,5 +8,5 @@ import androidx.annotation.UiThread; interface TransportStateListener { @UiThread - void stateUpdate(TransportId id, boolean enabled); + void stateUpdate(TransportId id, State state); } From 706c03aa8b7f41e05a55eb1fb5e86881df38ad2e Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 20 Jan 2020 15:40:24 +0000 Subject: [PATCH 21/69] Skip fetching RSS feeds if Tor is not active. --- .../briar/feed/FeedManagerImpl.java | 13 +++++++++++++ .../briar/feed/FeedManagerImplTest.java | 18 +++++++++++++----- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java index b7dfa5783..a1c0249f8 100644 --- a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java @@ -25,6 +25,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.TorConstants; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.event.TransportActiveEvent; +import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent; import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.system.Clock; @@ -97,6 +98,8 @@ class FeedManagerImpl implements FeedManager, EventListener, OpenDatabaseHook, private final Dns noDnsLookups; private final AtomicBoolean fetcherStarted = new AtomicBoolean(false); + private volatile boolean torActive = false; + @Inject FeedManagerImpl(@Scheduler ScheduledExecutorService scheduler, @IoExecutor Executor ioExecutor, DatabaseComponent db, @@ -123,11 +126,20 @@ class FeedManagerImpl implements FeedManager, EventListener, OpenDatabaseHook, if (e instanceof TransportActiveEvent) { TransportId t = ((TransportActiveEvent) e).getTransportId(); if (t.equals(TorConstants.ID)) { + setTorActive(true); startFeedExecutor(); } + } else if (e instanceof TransportInactiveEvent) { + TransportId t = ((TransportInactiveEvent) e).getTransportId(); + if (t.equals(TorConstants.ID)) setTorActive(false); } } + // Package access for testing + void setTorActive(boolean active) { + torActive = active; + } + private void startFeedExecutor() { if (fetcherStarted.getAndSet(true)) return; LOG.info("Tor started, scheduling RSS feed fetcher"); @@ -279,6 +291,7 @@ class FeedManagerImpl implements FeedManager, EventListener, OpenDatabaseHook, * and we can not block the database that long. */ void fetchFeeds() { + if (!torActive) return; LOG.info("Updating RSS feeds..."); // Get current feeds diff --git a/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerImplTest.java b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerImplTest.java index 48a203710..fc9c14ef4 100644 --- a/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerImplTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerImplTest.java @@ -76,15 +76,22 @@ public class FeedManagerImplTest extends BrambleMockTestCase { SocketFactory.getDefault(), clock, noDnsLookups); @Test - public void testEmptyFetchFeed() throws Exception { - BdfList feedList = new BdfList(); - expectGetFeeds(feedList); - expectStoreFeed(feedList); + public void testFetchFeedsReturnsEarlyIfTorIsNotActive() { + feedManager.setTorActive(false); feedManager.fetchFeeds(); } @Test - public void testFetchFeedIoException() throws Exception { + public void testEmptyFetchFeeds() throws Exception { + BdfList feedList = new BdfList(); + expectGetFeeds(feedList); + expectStoreFeed(feedList); + feedManager.setTorActive(true); + feedManager.fetchFeeds(); + } + + @Test + public void testFetchFeedsIoException() throws Exception { BdfDictionary feedDict= new BdfDictionary(); BdfList feedList = BdfList.of(feedDict); @@ -95,6 +102,7 @@ public class FeedManagerImplTest extends BrambleMockTestCase { }}); expectStoreFeed(feedList); + feedManager.setTorActive(true); feedManager.fetchFeeds(); } From 4f54bd90fb143e544ae90e7553c5496d7aff2fa1 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 16 Jan 2020 14:09:22 +0000 Subject: [PATCH 22/69] Remove redundant casts. --- .../android/settings/SettingsFragment.java | 38 ++++++++----------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java index dc7096db0..ed071d8ec 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java @@ -163,28 +163,21 @@ public class SettingsFragment extends PreferenceFragmentCompat public void onCreatePreferences(Bundle bundle, String s) { addPreferencesFromResource(R.xml.settings); - language = (ListPreference) findPreference(LANGUAGE); + language = findPreference(LANGUAGE); setLanguageEntries(); - ListPreference theme = - (ListPreference) findPreference("pref_key_theme"); - enableBluetooth = (ListPreference) findPreference("pref_key_bluetooth"); - torNetwork = (ListPreference) findPreference(TOR_NETWORK); - torMobile = (SwitchPreference) findPreference(TOR_MOBILE); - torOnlyWhenCharging = - (SwitchPreference) findPreference(TOR_ONLY_WHEN_CHARGING); - screenLock = (SwitchPreference) findPreference(PREF_SCREEN_LOCK); - screenLockTimeout = - (ListPreference) findPreference(PREF_SCREEN_LOCK_TIMEOUT); - notifyPrivateMessages = (SwitchPreference) findPreference( - "pref_key_notify_private_messages"); - notifyGroupMessages = (SwitchPreference) findPreference( - "pref_key_notify_group_messages"); - notifyForumPosts = (SwitchPreference) findPreference( - "pref_key_notify_forum_posts"); - notifyBlogPosts = (SwitchPreference) findPreference( - "pref_key_notify_blog_posts"); - notifyVibration = (SwitchPreference) findPreference( - "pref_key_notify_vibration"); + ListPreference theme = findPreference("pref_key_theme"); + enableBluetooth = findPreference("pref_key_bluetooth"); + torNetwork = findPreference(TOR_NETWORK); + torMobile = findPreference(TOR_MOBILE); + torOnlyWhenCharging = findPreference(TOR_ONLY_WHEN_CHARGING); + screenLock = findPreference(PREF_SCREEN_LOCK); + screenLockTimeout = findPreference(PREF_SCREEN_LOCK_TIMEOUT); + notifyPrivateMessages = + findPreference("pref_key_notify_private_messages"); + notifyGroupMessages = findPreference("pref_key_notify_group_messages"); + notifyForumPosts = findPreference("pref_key_notify_forum_posts"); + notifyBlogPosts = findPreference("pref_key_notify_blog_posts"); + notifyVibration = findPreference("pref_key_notify_vibration"); notifySound = findPreference("pref_key_notify_sound"); language.setOnPreferenceChangeListener(this); @@ -335,7 +328,8 @@ public class SettingsFragment extends PreferenceFragmentCompat boolean blocked = circumventionProvider.isTorProbablyBlocked(country); boolean useBridges = circumventionProvider.doBridgesWork(country); - String setting = getString(R.string.tor_network_setting_without_bridges); + String setting = + getString(R.string.tor_network_setting_without_bridges); if (blocked && useBridges) { setting = getString(R.string.tor_network_setting_with_bridges); } else if (blocked) { From 63e3c661a3f6a4eeec77c2c9b09274dbf94417c7 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 16 Jan 2020 14:41:03 +0000 Subject: [PATCH 23/69] Add toggle setting for Tor plugin. --- .../bramble/api/plugin/TorConstants.java | 1 + .../bramble/plugin/tor/TorPlugin.java | 22 ++++++++-- .../android/settings/SettingsFragment.java | 44 ++++++++++++++----- briar-android/src/main/res/values/arrays.xml | 2 - briar-android/src/main/res/values/strings.xml | 7 +-- briar-android/src/main/res/xml/settings.xml | 8 ++++ 6 files changed, 66 insertions(+), 18 deletions(-) diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java index 2d808eaa9..6199ddd35 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java @@ -13,6 +13,7 @@ public interface TorConstants { int CONNECT_TO_PROXY_TIMEOUT = 5000; // Milliseconds int EXTRA_SOCKET_TIMEOUT = 30000; // Milliseconds + String PREF_TOR_ENABLE = "enable"; String PREF_TOR_NETWORK = "network2"; String PREF_TOR_PORT = "port"; String PREF_TOR_MOBILE = "useMobileData"; diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index d20901002..b224e09b9 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -75,6 +75,7 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; import static org.briarproject.bramble.api.plugin.TorConstants.CONTROL_PORT; import static org.briarproject.bramble.api.plugin.TorConstants.ID; +import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ENABLE; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_AUTOMATIC; @@ -200,7 +201,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } } // Load the settings - settings = callback.getSettings(); + settings = migrateSettings(callback.getSettings()); // Install or update the assets if necessary if (!assetsAreUpToDate()) installAssets(); if (cookieFile.exists() && !cookieFile.delete()) @@ -288,6 +289,18 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { bind(); } + // TODO: Remove after a reasonable migration period (added 2020-01-16) + private Settings migrateSettings(Settings settings) { + int network = settings.getInt(PREF_TOR_NETWORK, + PREF_TOR_NETWORK_AUTOMATIC); + if (network == PREF_TOR_NETWORK_NEVER) { + settings.putInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_AUTOMATIC); + settings.putBoolean(PREF_TOR_ENABLE, false); + callback.mergeSettings(settings); + } + return settings; + } + private boolean assetsAreUpToDate() { return doneFile.lastModified() > getLastUpdateTime(); } @@ -763,6 +776,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { String country = locationUtils.getCurrentCountry(); boolean blocked = circumventionProvider.isTorProbablyBlocked(country); + boolean enabledByUser = settings.getBoolean(PREF_TOR_ENABLE, true); int network = settings.getInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_AUTOMATIC); boolean useMobile = settings.getBoolean(PREF_TOR_MOBILE, true); @@ -785,7 +799,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { if (!online) { LOG.info("Disabling network, device is offline"); - } else if (network == PREF_TOR_NETWORK_NEVER) { + } else if (!enabledByUser) { LOG.info("Disabling network, user has disabled Tor"); disabledBySettings = true; reasonDisabled = REASON_USER; @@ -870,11 +884,11 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { callback.pluginStateChanged(getState()); } + // Doesn't affect getState() synchronized void setTorStarted() { torStarted = true; } - @SuppressWarnings("BooleanMethodIsAlwaysInverted") synchronized boolean isTorRunning() { return torStarted && !stopped; } @@ -914,12 +928,14 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { callback.pluginStateChanged(getState()); } + // Doesn't affect getState() synchronized boolean setServerSocket(ServerSocket ss) { if (stopped || serverSocket != null) return false; serverSocket = ss; return true; } + // Doesn't affect getState() synchronized void clearServerSocket(ServerSocket ss) { if (serverSocket == ss) serverSocket = null; } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java index ed071d8ec..00c3ef84f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java @@ -73,6 +73,7 @@ import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_LTR; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENABLE; +import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ENABLE; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_AUTOMATIC; @@ -105,16 +106,19 @@ public class SettingsFragment extends PreferenceFragmentCompat implements EventListener, OnPreferenceChangeListener { public static final String SETTINGS_NAMESPACE = "android-ui"; - public static final String BT_NAMESPACE = BluetoothConstants.ID.getString(); - public static final String TOR_NAMESPACE = TorConstants.ID.getString(); public static final String LANGUAGE = "pref_key_language"; public static final String PREF_SCREEN_LOCK = "pref_key_lock"; public static final String PREF_SCREEN_LOCK_TIMEOUT = "pref_key_lock_timeout"; public static final String NOTIFY_SIGN_IN = "pref_key_notify_sign_in"; - public static final String TOR_NETWORK = "pref_key_tor_network"; - public static final String TOR_MOBILE = "pref_key_tor_mobile_data"; - public static final String TOR_ONLY_WHEN_CHARGING = + + private static final String BT_NAMESPACE = + BluetoothConstants.ID.getString(); + private static final String TOR_NAMESPACE = TorConstants.ID.getString(); + private static final String TOR_ENABLE = "pref_key_tor_enable"; + private static final String TOR_NETWORK = "pref_key_tor_network"; + private static final String TOR_MOBILE = "pref_key_tor_mobile_data"; + private static final String TOR_ONLY_WHEN_CHARGING = "pref_key_tor_only_when_charging"; private static final Logger LOG = @@ -123,6 +127,7 @@ public class SettingsFragment extends PreferenceFragmentCompat private SettingsActivity listener; private ListPreference language; private ListPreference enableBluetooth; + private SwitchPreference enableTor; private ListPreference torNetwork; private SwitchPreference torMobile; private SwitchPreference torOnlyWhenCharging; @@ -167,6 +172,7 @@ public class SettingsFragment extends PreferenceFragmentCompat setLanguageEntries(); ListPreference theme = findPreference("pref_key_theme"); enableBluetooth = findPreference("pref_key_bluetooth"); + enableTor = findPreference(TOR_ENABLE); torNetwork = findPreference(TOR_NETWORK); torMobile = findPreference(TOR_MOBILE); torOnlyWhenCharging = findPreference(TOR_ONLY_WHEN_CHARGING); @@ -187,8 +193,7 @@ public class SettingsFragment extends PreferenceFragmentCompat UiUtils.setTheme(getActivity(), (String) newValue); // bring up parent activity, so it can change its theme as well // upstream bug: https://issuetracker.google.com/issues/38352704 - Intent intent = - new Intent(getActivity(), ENTRY_ACTIVITY); + Intent intent = new Intent(getActivity(), ENTRY_ACTIVITY); intent.setFlags( FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK); startActivity(intent); @@ -200,6 +205,7 @@ public class SettingsFragment extends PreferenceFragmentCompat return true; }); enableBluetooth.setOnPreferenceChangeListener(this); + enableTor.setOnPreferenceChangeListener(this); torNetwork.setOnPreferenceChangeListener(this); torMobile.setOnPreferenceChangeListener(this); torOnlyWhenCharging.setOnPreferenceChangeListener(this); @@ -365,6 +371,10 @@ public class SettingsFragment extends PreferenceFragmentCompat btSettings.getBoolean(PREF_BT_ENABLE, false); enableBluetooth.setValue(Boolean.toString(btEnabledSetting)); + boolean torEnabledSetting = + torSettings.getBoolean(PREF_TOR_ENABLE, true); + enableTor.setChecked(torEnabledSetting); + int torNetworkSetting = torSettings.getInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_AUTOMATIC); torNetwork.setValue(Integer.toString(torNetworkSetting)); @@ -437,9 +447,10 @@ public class SettingsFragment extends PreferenceFragmentCompat // - pref_key_lock (screenLock -> displayScreenLockSetting()) // - pref_key_lock_timeout (screenLockTimeout) enableBluetooth.setEnabled(enabled); - torNetwork.setEnabled(enabled); - torMobile.setEnabled(enabled); - torOnlyWhenCharging.setEnabled(enabled); + enableTor.setEnabled(enabled); + torNetwork.setEnabled(enabled && enableTor.isChecked()); + torMobile.setEnabled(enabled && enableTor.isChecked()); + torOnlyWhenCharging.setEnabled(enabled && enableTor.isChecked()); if (!enabled) screenLock.setEnabled(false); notifyPrivateMessages.setEnabled(enabled); notifyGroupMessages.setEnabled(enabled); @@ -541,6 +552,13 @@ public class SettingsFragment extends PreferenceFragmentCompat } else if (preference == enableBluetooth) { boolean btSetting = Boolean.valueOf((String) newValue); storeBluetoothSettings(btSetting); + } else if (preference == enableTor) { + boolean torEnabledSetting = (Boolean) newValue; + torNetwork.setEnabled(torNetwork.isEnabled() && torEnabledSetting); + torMobile.setEnabled(torMobile.isEnabled() && torEnabledSetting); + torOnlyWhenCharging.setEnabled(torOnlyWhenCharging.isEnabled() && + torEnabledSetting); + storeTorEnabledSetting(torEnabledSetting); } else if (preference == torNetwork) { int torNetworkSetting = Integer.valueOf((String) newValue); storeTorNetworkSetting(torNetworkSetting); @@ -604,6 +622,12 @@ public class SettingsFragment extends PreferenceFragmentCompat builder.show(); } + private void storeTorEnabledSetting(boolean torEnabledSetting) { + Settings s = new Settings(); + s.putBoolean(PREF_TOR_ENABLE, torEnabledSetting); + mergeSettings(s, TOR_NAMESPACE); + } + private void storeTorNetworkSetting(int torNetworkSetting) { Settings s = new Settings(); s.putInt(PREF_TOR_NETWORK, torNetworkSetting); diff --git a/briar-android/src/main/res/values/arrays.xml b/briar-android/src/main/res/values/arrays.xml index a7a950e78..3ed985eec 100644 --- a/briar-android/src/main/res/values/arrays.xml +++ b/briar-android/src/main/res/values/arrays.xml @@ -13,13 +13,11 @@ @string/tor_network_setting_automatic @string/tor_network_setting_without_bridges @string/tor_network_setting_with_bridges - @string/tor_network_setting_never 0 1 2 - 3 diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index 341e2c4ab..fe20d9e95 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -443,12 +443,13 @@ Automatic (Daytime) System Default - - Networks + + Connections Connect via Bluetooth Whenever contacts are nearby Only when adding contacts - Connect via Internet (Tor) + Connect to contacts via Internet (Tor) + Connection method for Internet (Tor) Automatic based on location Use Tor without bridges Use Tor with bridges diff --git a/briar-android/src/main/res/xml/settings.xml b/briar-android/src/main/res/xml/settings.xml index d45a80f86..b6c0b86ce 100644 --- a/briar-android/src/main/res/xml/settings.xml +++ b/briar-android/src/main/res/xml/settings.xml @@ -39,6 +39,14 @@ android:title="@string/bluetooth_setting" app:iconSpaceReserved="false"/> + + Date: Thu, 16 Jan 2020 14:51:32 +0000 Subject: [PATCH 24/69] Convert Bluetooth setting to a switch. --- .../briar/android/settings/SettingsFragment.java | 10 ++++++---- briar-android/src/main/res/values/arrays.xml | 10 +--------- briar-android/src/main/res/values/strings.xml | 4 +--- briar-android/src/main/res/xml/settings.xml | 6 ++---- 4 files changed, 10 insertions(+), 20 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java index 00c3ef84f..2d469d5ce 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java @@ -114,6 +114,8 @@ public class SettingsFragment extends PreferenceFragmentCompat private static final String BT_NAMESPACE = BluetoothConstants.ID.getString(); + private static final String BT_ENABLE = "pref_key_bluetooth"; + private static final String TOR_NAMESPACE = TorConstants.ID.getString(); private static final String TOR_ENABLE = "pref_key_tor_enable"; private static final String TOR_NETWORK = "pref_key_tor_network"; @@ -126,7 +128,7 @@ public class SettingsFragment extends PreferenceFragmentCompat private SettingsActivity listener; private ListPreference language; - private ListPreference enableBluetooth; + private SwitchPreference enableBluetooth; private SwitchPreference enableTor; private ListPreference torNetwork; private SwitchPreference torMobile; @@ -171,7 +173,7 @@ public class SettingsFragment extends PreferenceFragmentCompat language = findPreference(LANGUAGE); setLanguageEntries(); ListPreference theme = findPreference("pref_key_theme"); - enableBluetooth = findPreference("pref_key_bluetooth"); + enableBluetooth = findPreference(BT_ENABLE); enableTor = findPreference(TOR_ENABLE); torNetwork = findPreference(TOR_NETWORK); torMobile = findPreference(TOR_MOBILE); @@ -369,7 +371,7 @@ public class SettingsFragment extends PreferenceFragmentCompat boolean btEnabledSetting = btSettings.getBoolean(PREF_BT_ENABLE, false); - enableBluetooth.setValue(Boolean.toString(btEnabledSetting)); + enableBluetooth.setChecked(btEnabledSetting); boolean torEnabledSetting = torSettings.getBoolean(PREF_TOR_ENABLE, true); @@ -550,7 +552,7 @@ public class SettingsFragment extends PreferenceFragmentCompat languageChanged((String) newValue); return false; } else if (preference == enableBluetooth) { - boolean btSetting = Boolean.valueOf((String) newValue); + boolean btSetting = (Boolean) newValue; storeBluetoothSettings(btSetting); } else if (preference == enableTor) { boolean torEnabledSetting = (Boolean) newValue; diff --git a/briar-android/src/main/res/values/arrays.xml b/briar-android/src/main/res/values/arrays.xml index 3ed985eec..8a7313b73 100644 --- a/briar-android/src/main/res/values/arrays.xml +++ b/briar-android/src/main/res/values/arrays.xml @@ -1,14 +1,5 @@ - - true - false - - - @string/bluetooth_setting_enabled - @string/bluetooth_setting_disabled - - @string/tor_network_setting_automatic @string/tor_network_setting_without_bridges @@ -64,6 +55,7 @@ zh-CN zh-TW + @string/pref_theme_light @string/pref_theme_dark diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index fe20d9e95..dea0d1b98 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -445,9 +445,7 @@ Connections - Connect via Bluetooth - Whenever contacts are nearby - Only when adding contacts + Connect to contacts via Bluetooth Connect to contacts via Internet (Tor) Connection method for Internet (Tor) Automatic based on location diff --git a/briar-android/src/main/res/xml/settings.xml b/briar-android/src/main/res/xml/settings.xml index b6c0b86ce..f06bd78d7 100644 --- a/briar-android/src/main/res/xml/settings.xml +++ b/briar-android/src/main/res/xml/settings.xml @@ -29,14 +29,12 @@ android:layout="@layout/preferences_category" android:title="@string/network_settings_title"> - Date: Thu, 16 Jan 2020 15:50:44 +0000 Subject: [PATCH 25/69] Update semantics of Bluetooth setting. The setting now enables/disables the plugin, not just contact connections. The key agreement UI will need to be updated to change the setting if the user agrees to use Bluetooth. --- .../api/plugin/BluetoothConstants.java | 3 + .../plugin/bluetooth/BluetoothPlugin.java | 97 ++++++++++++------- 2 files changed, 64 insertions(+), 36 deletions(-) diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java index 785aee2b3..9176bdeb4 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java @@ -10,4 +10,7 @@ public interface BluetoothConstants { String PROP_UUID = "uuid"; String PREF_BT_ENABLE = "enable"; + + int REASON_USER = 1; + int REASON_NO_BT_ADAPTER = 2; } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java index 4de8cc97e..111ea7894 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java @@ -10,6 +10,7 @@ import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection; import org.briarproject.bramble.api.keyagreement.KeyAgreementListener; import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent; import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStoppedListeningEvent; +import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; @@ -49,6 +50,7 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENABLE; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID; +import static org.briarproject.bramble.api.plugin.BluetoothConstants.REASON_NO_BT_ADAPTER; import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; @@ -78,7 +80,6 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { protected final PluginState state = new PluginState(); - private volatile boolean contactConnections = false; private volatile String contactConnectionsUuid = null; abstract void initialiseAdapter() throws IOException; @@ -131,16 +132,18 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { LOG.info("Bluetooth enabled"); // We may not have been able to get the local address before ioExecutor.execute(this::updateProperties); - if (shouldAllowContactConnections()) bind(); - callback.pluginStateChanged(getState()); + if (getState() == INACTIVE) bind(); } void onAdapterDisabled() { LOG.info("Bluetooth disabled"); connectionLimiter.allConnectionsClosed(); // The server socket may not have been closed automatically - tryToClose(state.clearServerSocket()); - callback.pluginStateChanged(getState()); + SS ss = state.clearServerSocket(); + if (ss != null) { + LOG.info("Closing server socket"); + tryToClose(ss); + } } @Override @@ -164,29 +167,22 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { try { initialiseAdapter(); } catch (IOException e) { + state.setNoAdapter(); throw new PluginException(e); } updateProperties(); - state.setStarted(); - loadSettings(callback.getSettings()); - if (shouldAllowContactConnections()) { + Settings settings = callback.getSettings(); + boolean enabledByUser = settings.getBoolean(PREF_BT_ENABLE, false); + state.setStarted(enabledByUser); + if (enabledByUser) { if (isAdapterEnabled()) bind(); else enableAdapter(); } } - private void loadSettings(Settings settings) { - contactConnections = settings.getBoolean(PREF_BT_ENABLE, false); - } - - private boolean shouldAllowContactConnections() { - return contactConnections; - } - private void bind() { ioExecutor.execute(() -> { - if (!shouldAllowContactConnections() || getState() != ACTIVE) - return; + if (getState() != INACTIVE) return; // Bind a server socket to accept connections from contacts SS ss; try { @@ -195,8 +191,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { logException(LOG, WARNING, e); return; } - if (!shouldAllowContactConnections() || - !state.setServerSocket(ss)) { + if (!state.setServerSocket(ss)) { LOG.info("Closing redundant server socket"); tryToClose(ss); return; @@ -263,7 +258,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { @Override public int getReasonDisabled() { - return getState() == DISABLED ? REASON_STARTING_STOPPING : -1; + return state.getReasonDisabled(); } @Override @@ -279,7 +274,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { @Override public void poll(Collection> properties) { - if (!shouldAllowContactConnections() || getState() != ACTIVE) return; + if (getState() != ACTIVE) return; backoff.increment(); for (Pair p : properties) { connect(p.getFirst(), p.getSecond()); @@ -333,8 +328,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { @Override public DuplexTransportConnection createConnection(TransportProperties p) { - if (!shouldAllowContactConnections() || getState() != ACTIVE) - return null; + if (getState() != ACTIVE) return null; if (!connectionLimiter.canOpenContactConnection()) return null; String address = p.get(PROP_ADDRESS); if (isNullOrEmpty(address)) return null; @@ -438,16 +432,18 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { } } + @IoExecutor private void onSettingsUpdated(Settings settings) { - boolean wasAllowed = shouldAllowContactConnections(); - loadSettings(settings); - boolean isAllowed = shouldAllowContactConnections(); - if (wasAllowed && !isAllowed) { - LOG.info("Contact connections disabled"); - tryToClose(state.clearServerSocket()); + boolean enabledByUser = settings.getBoolean(PREF_BT_ENABLE, false); + SS ss = state.setEnabledByUser(enabledByUser); + State s = getState(); + callback.pluginStateChanged(s); + if (ss != null) { + LOG.info("Disabled by user, closing server socket"); + tryToClose(ss); disableAdapterIfEnabledByUs(); - } else if (!wasAllowed && isAllowed) { - LOG.info("Contact connections enabled"); + } else if (s == INACTIVE) { + LOG.info("Enabled by user, opening server socket"); if (isAdapterEnabled()) bind(); else enableAdapter(); } @@ -481,13 +477,18 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { protected class PluginState { @GuardedBy("this") - private boolean started = false, stopped = false; + private boolean started = false, + stopped = false, + noAdapter = false, + enabledByUser = false; + @GuardedBy("this") @Nullable private SS serverSocket = null; - synchronized void setStarted() { + synchronized void setStarted(boolean enabledByUser) { started = true; + this.enabledByUser = enabledByUser; callback.pluginStateChanged(getState()); } @@ -500,6 +501,23 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { return ss; } + synchronized void setNoAdapter() { + noAdapter = true; + callback.pluginStateChanged(getState()); + } + + @Nullable + synchronized SS setEnabledByUser(boolean enabledByUser) { + this.enabledByUser = enabledByUser; + SS ss = null; + if (!enabledByUser) { + ss = serverSocket; + serverSocket = null; + } + callback.pluginStateChanged(getState()); + return ss; + } + synchronized boolean setServerSocket(SS ss) { if (stopped || serverSocket != null) return false; serverSocket = ss; @@ -511,12 +529,19 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { synchronized SS clearServerSocket() { SS ss = serverSocket; serverSocket = null; + callback.pluginStateChanged(getState()); return ss; } synchronized State getState() { - if (!started || stopped) return DISABLED; - return isAdapterEnabled() ? ACTIVE : INACTIVE; + if (!started || stopped || !enabledByUser) return DISABLED; + return serverSocket == null ? INACTIVE : ACTIVE; + } + + synchronized int getReasonDisabled() { + if (noAdapter && !stopped) return REASON_NO_BT_ADAPTER; + if (!started || stopped) return REASON_STARTING_STOPPING; + return enabledByUser ? -1 : REASON_USER; } } } From 03811f78fa321530f8a3600e352907d0f2b5cb3b Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 16 Jan 2020 17:51:49 +0000 Subject: [PATCH 26/69] Add toggle setting for LAN plugin. --- .../plugin/tcp/AndroidLanTcpPlugin.java | 9 ++- .../bramble/api/plugin/TcpConstants.java | 8 +++ .../bramble/plugin/tcp/LanTcpPlugin.java | 3 +- .../plugin/tcp/LanTcpPluginFactory.java | 9 ++- .../bramble/plugin/tcp/TcpPlugin.java | 63 +++++++++++++++++-- .../plugin/tcp/WanTcpPluginFactory.java | 9 ++- .../bramble/plugin/DesktopPluginModule.java | 4 +- .../android/settings/SettingsFragment.java | 33 +++++++++- briar-android/src/main/res/values/strings.xml | 1 + briar-android/src/main/res/xml/settings.xml | 8 +++ 10 files changed, 128 insertions(+), 19 deletions(-) create mode 100644 bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TcpConstants.java 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 f269d3406..ffbeab9d3 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 @@ -9,11 +9,11 @@ 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.PluginCallback; +import org.briarproject.bramble.api.settings.Settings; import java.io.IOException; import java.net.InetAddress; @@ -36,10 +36,11 @@ import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; +import static org.briarproject.bramble.api.plugin.TcpConstants.PREF_TCP_ENABLE; import static org.briarproject.bramble.util.IoUtils.tryToClose; @NotNullByDefault -class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener { +class AndroidLanTcpPlugin extends LanTcpPlugin { private static final Logger LOG = getLogger(AndroidLanTcpPlugin.class.getName()); @@ -72,7 +73,8 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener { public void start() { if (used.getAndSet(true)) throw new IllegalStateException(); initialisePortProperty(); - state.setStarted(); + Settings settings = callback.getSettings(); + state.setStarted(settings.getBoolean(PREF_TCP_ENABLE, false)); updateConnectionStatus(); } @@ -128,6 +130,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener { @Override public void eventOccurred(Event e) { + super.eventOccurred(e); if (e instanceof NetworkStatusEvent) updateConnectionStatus(); } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TcpConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TcpConstants.java new file mode 100644 index 000000000..8e008b0ae --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TcpConstants.java @@ -0,0 +1,8 @@ +package org.briarproject.bramble.api.plugin; + +public interface TcpConstants { + + String PREF_TCP_ENABLE = "enable"; + + int REASON_USER = 1; +} diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java index bd980cff6..1200b10b1 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java @@ -91,7 +91,8 @@ class LanTcpPlugin extends TcpPlugin { public void start() { if (used.getAndSet(true)) throw new IllegalStateException(); initialisePortProperty(); - running = true; + Settings settings = callback.getSettings(); + state.setStarted(settings.getBoolean(PREF_PLUGIN_ENABLE, false)); bind(); } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginFactory.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginFactory.java index cfbdcea80..6528a6571 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginFactory.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginFactory.java @@ -1,5 +1,6 @@ package org.briarproject.bramble.plugin.tcp; +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; @@ -26,11 +27,13 @@ public class LanTcpPluginFactory implements DuplexPluginFactory { private static final double BACKOFF_BASE = 1.2; private final Executor ioExecutor; + private final EventBus eventBus; private final BackoffFactory backoffFactory; - public LanTcpPluginFactory(Executor ioExecutor, + public LanTcpPluginFactory(Executor ioExecutor, EventBus eventBus, BackoffFactory backoffFactory) { this.ioExecutor = ioExecutor; + this.eventBus = eventBus; this.backoffFactory = backoffFactory; } @@ -48,7 +51,9 @@ public class LanTcpPluginFactory implements DuplexPluginFactory { public DuplexPlugin createPlugin(PluginCallback callback) { Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE); - return new LanTcpPlugin(ioExecutor, backoff, callback, MAX_LATENCY, + LanTcpPlugin plugin = new LanTcpPlugin(ioExecutor, backoff, callback, MAX_LATENCY, MAX_IDLE_TIME, CONNECTION_TIMEOUT); + eventBus.addListener(plugin); + return plugin; } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java index 35f291633..c8c650af6 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java @@ -3,7 +3,10 @@ package org.briarproject.bramble.plugin.tcp; import org.briarproject.bramble.PoliteExecutor; import org.briarproject.bramble.api.Pair; 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.lifecycle.IoExecutor; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; @@ -15,6 +18,8 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.rendezvous.KeyMaterialSource; import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint; +import org.briarproject.bramble.api.settings.Settings; +import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent; import java.io.IOException; import java.net.InetAddress; @@ -46,6 +51,8 @@ import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; +import static org.briarproject.bramble.api.plugin.TcpConstants.PREF_TCP_ENABLE; +import static org.briarproject.bramble.api.plugin.TcpConstants.REASON_USER; import static org.briarproject.bramble.util.IoUtils.tryToClose; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress; @@ -53,7 +60,7 @@ import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; @MethodsNotNullByDefault @ParametersNotNullByDefault -abstract class TcpPlugin implements DuplexPlugin { +abstract class TcpPlugin implements DuplexPlugin, EventListener { private static final Logger LOG = getLogger(TcpPlugin.class.getName()); @@ -122,7 +129,8 @@ abstract class TcpPlugin implements DuplexPlugin { @Override public void start() { if (used.getAndSet(true)) throw new IllegalStateException(); - state.setStarted(); + Settings settings = callback.getSettings(); + state.setStarted(settings.getBoolean(PREF_TCP_ENABLE, false)); bind(); } @@ -200,7 +208,7 @@ abstract class TcpPlugin implements DuplexPlugin { @Override public int getReasonDisabled() { - return getState() == DISABLED ? REASON_STARTING_STOPPING : -1; + return state.getReasonDisabled(); } @Override @@ -370,18 +378,44 @@ abstract class TcpPlugin implements DuplexPlugin { } } + @Override + public void eventOccurred(Event e) { + if (e instanceof SettingsUpdatedEvent) { + SettingsUpdatedEvent s = (SettingsUpdatedEvent) e; + if (s.getNamespace().equals(getId().getString())) + ioExecutor.execute(() -> onSettingsUpdated(s.getSettings())); + } + } + + @IoExecutor + private void onSettingsUpdated(Settings settings) { + boolean enabledByUser = settings.getBoolean(PREF_TCP_ENABLE, false); + ServerSocket ss = state.setEnabledByUser(enabledByUser); + State s = getState(); + callback.pluginStateChanged(s); + if (ss != null) { + LOG.info("Disabled by user, closing server socket"); + tryToClose(ss, LOG, WARNING); + } else if (s == INACTIVE) { + LOG.info("Enabled by user, opening server socket"); + bind(); + } + } + @ThreadSafe @NotNullByDefault protected class PluginState { @GuardedBy("this") - private boolean started = false, stopped = false; + private boolean started = false, stopped = false, enabledByUser = false; + @GuardedBy("this") @Nullable private ServerSocket serverSocket = null; - synchronized void setStarted() { + synchronized void setStarted(boolean enabledByUser) { started = true; + this.enabledByUser = enabledByUser; callback.pluginStateChanged(getState()); } @@ -394,6 +428,18 @@ abstract class TcpPlugin implements DuplexPlugin { return ss; } + @Nullable + synchronized ServerSocket setEnabledByUser(boolean enabledByUser) { + this.enabledByUser = enabledByUser; + ServerSocket ss = null; + if (!enabledByUser) { + ss = serverSocket; + serverSocket = null; + } + callback.pluginStateChanged(getState()); + return ss; + } + @Nullable synchronized ServerSocket getServerSocket() { return serverSocket; @@ -412,8 +458,13 @@ abstract class TcpPlugin implements DuplexPlugin { } synchronized State getState() { - if (!started || stopped) return DISABLED; + if (!started || stopped || !enabledByUser) return DISABLED; return serverSocket == null ? INACTIVE : ACTIVE; } + + synchronized int getReasonDisabled() { + if (!started || stopped) return REASON_STARTING_STOPPING; + return enabledByUser ? -1 : REASON_USER; + } } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/WanTcpPluginFactory.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/WanTcpPluginFactory.java index 079697319..db0f2c2a8 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/WanTcpPluginFactory.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/WanTcpPluginFactory.java @@ -1,5 +1,6 @@ package org.briarproject.bramble.plugin.tcp; +import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.lifecycle.ShutdownManager; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.Backoff; @@ -27,12 +28,14 @@ public class WanTcpPluginFactory implements DuplexPluginFactory { private static final double BACKOFF_BASE = 1.2; private final Executor ioExecutor; + private final EventBus eventBus; private final BackoffFactory backoffFactory; private final ShutdownManager shutdownManager; - public WanTcpPluginFactory(Executor ioExecutor, + public WanTcpPluginFactory(Executor ioExecutor, EventBus eventBus, BackoffFactory backoffFactory, ShutdownManager shutdownManager) { this.ioExecutor = ioExecutor; + this.eventBus = eventBus; this.backoffFactory = backoffFactory; this.shutdownManager = shutdownManager; } @@ -51,8 +54,10 @@ public class WanTcpPluginFactory implements DuplexPluginFactory { public DuplexPlugin createPlugin(PluginCallback callback) { Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE); - return new WanTcpPlugin(ioExecutor, backoff, + WanTcpPlugin plugin = new WanTcpPlugin(ioExecutor, backoff, new PortMapperImpl(shutdownManager), callback, MAX_LATENCY, MAX_IDLE_TIME, CONNECTION_TIMEOUT); + eventBus.addListener(plugin); + return plugin; } } diff --git a/bramble-java/src/main/java/org/briarproject/bramble/plugin/DesktopPluginModule.java b/bramble-java/src/main/java/org/briarproject/bramble/plugin/DesktopPluginModule.java index 53578ccc4..f7a5168b9 100644 --- a/bramble-java/src/main/java/org/briarproject/bramble/plugin/DesktopPluginModule.java +++ b/bramble-java/src/main/java/org/briarproject/bramble/plugin/DesktopPluginModule.java @@ -45,9 +45,9 @@ public class DesktopPluginModule extends PluginModule { ioExecutor, random, eventBus, timeoutMonitor, backoffFactory); DuplexPluginFactory modem = new ModemPluginFactory(ioExecutor, reliabilityFactory); - DuplexPluginFactory lan = new LanTcpPluginFactory(ioExecutor, + DuplexPluginFactory lan = new LanTcpPluginFactory(ioExecutor, eventBus, backoffFactory); - DuplexPluginFactory wan = new WanTcpPluginFactory(ioExecutor, + DuplexPluginFactory wan = new WanTcpPluginFactory(ioExecutor, eventBus, backoffFactory, shutdownManager); Collection duplex = asList(bluetooth, modem, lan, wan); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java index 2d469d5ce..0cc96c6f8 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java @@ -21,6 +21,7 @@ import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.plugin.BluetoothConstants; +import org.briarproject.bramble.api.plugin.LanTcpConstants; import org.briarproject.bramble.api.plugin.TorConstants; import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.SettingsManager; @@ -73,6 +74,7 @@ import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_LTR; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENABLE; +import static org.briarproject.bramble.api.plugin.TcpConstants.PREF_TCP_ENABLE; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ENABLE; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK; @@ -116,6 +118,9 @@ public class SettingsFragment extends PreferenceFragmentCompat BluetoothConstants.ID.getString(); private static final String BT_ENABLE = "pref_key_bluetooth"; + private static final String WIFI_NAMESPACE = LanTcpConstants.ID.getString(); + private static final String WIFI_ENABLE = "pref_key_wifi"; + private static final String TOR_NAMESPACE = TorConstants.ID.getString(); private static final String TOR_ENABLE = "pref_key_tor_enable"; private static final String TOR_NETWORK = "pref_key_tor_network"; @@ -129,6 +134,7 @@ public class SettingsFragment extends PreferenceFragmentCompat private SettingsActivity listener; private ListPreference language; private SwitchPreference enableBluetooth; + private SwitchPreference enableWifi; private SwitchPreference enableTor; private ListPreference torNetwork; private SwitchPreference torMobile; @@ -144,7 +150,7 @@ public class SettingsFragment extends PreferenceFragmentCompat private Preference notifySound; // Fields that are accessed from background threads must be volatile - private volatile Settings settings, btSettings, torSettings; + private volatile Settings settings, btSettings, wifiSettings, torSettings; private volatile boolean settingsLoaded = false; @Inject @@ -174,6 +180,7 @@ public class SettingsFragment extends PreferenceFragmentCompat setLanguageEntries(); ListPreference theme = findPreference("pref_key_theme"); enableBluetooth = findPreference(BT_ENABLE); + enableWifi = findPreference(WIFI_ENABLE); enableTor = findPreference(TOR_ENABLE); torNetwork = findPreference(TOR_NETWORK); torMobile = findPreference(TOR_MOBILE); @@ -207,6 +214,7 @@ public class SettingsFragment extends PreferenceFragmentCompat return true; }); enableBluetooth.setOnPreferenceChangeListener(this); + enableWifi.setOnPreferenceChangeListener(this); enableTor.setOnPreferenceChangeListener(this); torNetwork.setOnPreferenceChangeListener(this); torMobile.setOnPreferenceChangeListener(this); @@ -354,6 +362,7 @@ public class SettingsFragment extends PreferenceFragmentCompat long start = now(); settings = settingsManager.getSettings(SETTINGS_NAMESPACE); btSettings = settingsManager.getSettings(BT_NAMESPACE); + wifiSettings = settingsManager.getSettings(WIFI_NAMESPACE); torSettings = settingsManager.getSettings(TOR_NAMESPACE); settingsLoaded = true; logDuration(LOG, "Loading settings", start); @@ -373,6 +382,10 @@ public class SettingsFragment extends PreferenceFragmentCompat btSettings.getBoolean(PREF_BT_ENABLE, false); enableBluetooth.setChecked(btEnabledSetting); + boolean wifiEnabledSetting = + wifiSettings.getBoolean(PREF_TCP_ENABLE, false); + enableWifi.setChecked(wifiEnabledSetting); + boolean torEnabledSetting = torSettings.getBoolean(PREF_TOR_ENABLE, true); enableTor.setChecked(torEnabledSetting); @@ -449,6 +462,7 @@ public class SettingsFragment extends PreferenceFragmentCompat // - pref_key_lock (screenLock -> displayScreenLockSetting()) // - pref_key_lock_timeout (screenLockTimeout) enableBluetooth.setEnabled(enabled); + enableWifi.setEnabled(enabled); enableTor.setEnabled(enabled); torNetwork.setEnabled(enabled && enableTor.isChecked()); torMobile.setEnabled(enabled && enableTor.isChecked()); @@ -553,7 +567,10 @@ public class SettingsFragment extends PreferenceFragmentCompat return false; } else if (preference == enableBluetooth) { boolean btSetting = (Boolean) newValue; - storeBluetoothSettings(btSetting); + storeBluetoothSetting(btSetting); + } else if (preference == enableWifi) { + boolean wifiSetting = (Boolean) newValue; + storeWifiSetting(wifiSetting); } else if (preference == enableTor) { boolean torEnabledSetting = (Boolean) newValue; torNetwork.setEnabled(torNetwork.isEnabled() && torEnabledSetting); @@ -648,12 +665,18 @@ public class SettingsFragment extends PreferenceFragmentCompat mergeSettings(s, TOR_NAMESPACE); } - private void storeBluetoothSettings(boolean btSetting) { + private void storeBluetoothSetting(boolean btSetting) { Settings s = new Settings(); s.putBoolean(PREF_BT_ENABLE, btSetting); mergeSettings(s, BT_NAMESPACE); } + private void storeWifiSetting(boolean wifiSetting) { + Settings s = new Settings(); + s.putBoolean(PREF_TCP_ENABLE, wifiSetting); + mergeSettings(s, WIFI_NAMESPACE); + } + private void storeSettings(Settings s) { mergeSettings(s, SETTINGS_NAMESPACE); } @@ -716,6 +739,10 @@ public class SettingsFragment extends PreferenceFragmentCompat LOG.info("Bluetooth settings updated"); btSettings = s.getSettings(); displaySettings(); + } else if (namespace.equals(WIFI_NAMESPACE)) { + LOG.info("Wifi settings updated"); + wifiSettings = s.getSettings(); + displaySettings(); } else if (namespace.equals(TOR_NAMESPACE)) { LOG.info("Tor settings updated"); torSettings = s.getSettings(); diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index dea0d1b98..a6c6094b0 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -446,6 +446,7 @@ Connections Connect to contacts via Bluetooth + Connect to contacts on the same Wi-Fi network Connect to contacts via Internet (Tor) Connection method for Internet (Tor) Automatic based on location diff --git a/briar-android/src/main/res/xml/settings.xml b/briar-android/src/main/res/xml/settings.xml index f06bd78d7..b1b40552f 100644 --- a/briar-android/src/main/res/xml/settings.xml +++ b/briar-android/src/main/res/xml/settings.xml @@ -37,6 +37,14 @@ android:widgetLayout="@layout/preference_switch_compat" app:iconSpaceReserved="false"/> + + Date: Thu, 16 Jan 2020 17:56:31 +0000 Subject: [PATCH 27/69] Make REASON_USER into a generic reason code. --- .../bramble/api/plugin/BluetoothConstants.java | 2 +- .../briarproject/bramble/api/plugin/Plugin.java | 16 +++++++++++----- .../bramble/api/plugin/TcpConstants.java | 2 -- .../bramble/api/plugin/TorConstants.java | 2 +- .../bramble/plugin/tcp/TcpPlugin.java | 1 - .../bramble/plugin/tor/TorPlugin.java | 1 - 6 files changed, 13 insertions(+), 11 deletions(-) diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java index 9176bdeb4..915dfbbab 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java @@ -11,6 +11,6 @@ public interface BluetoothConstants { String PREF_BT_ENABLE = "enable"; - int REASON_USER = 1; + // Reason code returned by Plugin#getReasonDisabled() int REASON_NO_BT_ADAPTER = 2; } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java index b7247967c..ee26500f1 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java @@ -35,12 +35,18 @@ public interface Plugin { } /** - * Reason code returned by {@link #getReasonDisabled()} ()} to indicate - * that the plugin is disabled because it has not been started or has been + * Reason code returned by {@link #getReasonDisabled()} to indicate that + * the plugin is disabled because it has not been started or has been * stopped. */ int REASON_STARTING_STOPPING = 0; + /** + * Reason code returned by {@link #getReasonDisabled()} to indicate that + * the plugin has been disabled by the user. + */ + int REASON_USER = 1; + /** * Returns the plugin's transport identifier. */ @@ -75,9 +81,9 @@ public interface Plugin { * Returns an integer code indicating why the plugin is * {@link State#DISABLED disabled}, or -1 if the plugin is not disabled. *

- * The codes used are plugin-specific, except the generic code - * {@link #REASON_STARTING_STOPPING}, which may be used by - * any plugin. + * The codes used are plugin-specific, except the generic codes + * {@link #REASON_STARTING_STOPPING} and {@link #REASON_USER}, which may + * be used by any plugin. */ int getReasonDisabled(); diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TcpConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TcpConstants.java index 8e008b0ae..503f2cb88 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TcpConstants.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TcpConstants.java @@ -3,6 +3,4 @@ package org.briarproject.bramble.api.plugin; public interface TcpConstants { String PREF_TCP_ENABLE = "enable"; - - int REASON_USER = 1; } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java index 6199ddd35..727dab4b4 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java @@ -24,7 +24,7 @@ public interface TorConstants { int PREF_TOR_NETWORK_WITH_BRIDGES = 2; int PREF_TOR_NETWORK_NEVER = 3; - int REASON_USER = 1; + // Reason codes returned by Plugin#getReasonDisabled() int REASON_BATTERY = 2; int REASON_MOBILE_DATA = 3; int REASON_COUNTRY_BLOCKED = 4; diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java index c8c650af6..b5bd7cb4e 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java @@ -52,7 +52,6 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; import static org.briarproject.bramble.api.plugin.TcpConstants.PREF_TCP_ENABLE; -import static org.briarproject.bramble.api.plugin.TcpConstants.REASON_USER; import static org.briarproject.bramble.util.IoUtils.tryToClose; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress; diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index b224e09b9..afdeb6dd6 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -88,7 +88,6 @@ import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V3; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA; -import static org.briarproject.bramble.api.plugin.TorConstants.REASON_USER; import static org.briarproject.bramble.plugin.tor.TorRendezvousCrypto.SEED_BYTES; import static org.briarproject.bramble.util.IoUtils.copyAndClose; import static org.briarproject.bramble.util.IoUtils.tryToClose; From c67f758c90419b761c0156e0b172f50ccb13a420 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 17 Jan 2020 10:22:12 +0000 Subject: [PATCH 28/69] Small code cleanups in key agreement UI. --- .../keyagreement/ContactExchangeActivity.java | 13 +++++++-- .../keyagreement/KeyAgreementActivity.java | 29 +++++++++---------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeActivity.java index 062f12059..f18c41f75 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeActivity.java @@ -9,6 +9,7 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; +import org.briarproject.briar.android.fragment.BaseFragment; import javax.annotation.Nullable; import javax.inject.Inject; @@ -79,13 +80,13 @@ public class ContactExchangeActivity extends KeyAgreementActivity { @UiThread private void contactExchangeFailed() { - showErrorFragment(R.string.connection_error_explanation); + showErrorFragment(); } @UiThread @Override public void keyAgreementFailed() { - showErrorFragment(R.string.connection_error_explanation); + showErrorFragment(); } @UiThread @@ -103,7 +104,7 @@ public class ContactExchangeActivity extends KeyAgreementActivity { @UiThread @Override public void keyAgreementAborted(boolean remoteAborted) { - showErrorFragment(R.string.connection_error_explanation); + showErrorFragment(); } @UiThread @@ -112,4 +113,10 @@ public class ContactExchangeActivity extends KeyAgreementActivity { startContactExchange(result); return getString(R.string.exchanging_contact_details); } + + protected void showErrorFragment() { + String errorMsg = getString(R.string.connection_error_explanation); + BaseFragment f = ContactExchangeErrorFragment.newInstance(errorMsg); + showNextFragment(f); + } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java index 16d8e2a03..67e125020 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java @@ -44,6 +44,8 @@ import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE; import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE; import static android.bluetooth.BluetoothAdapter.STATE_ON; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static java.util.logging.Logger.getLogger; +import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_BLUETOOTH_DISCOVERABLE; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA_LOCATION; @@ -62,7 +64,7 @@ public abstract class KeyAgreementActivity extends BriarActivity implements } private static final Logger LOG = - Logger.getLogger(KeyAgreementActivity.class.getName()); + getLogger(KeyAgreementActivity.class.getName()); @Inject EventBus eventBus; @@ -74,18 +76,21 @@ public abstract class KeyAgreementActivity extends BriarActivity implements * https://issuetracker.google.com/issues/37067655. */ private boolean isResumed = false; + /** * Set to true when the continue button is clicked, and false when the QR * code fragment is shown. This prevents the QR code fragment from being * shown automatically before the continue button has been clicked. */ private boolean continueClicked = false; + /** * Records whether the Bluetooth adapter was already enabled before we * asked for Bluetooth discoverability, so we know whether to broadcast a * {@link BluetoothEnabledEvent}. */ private boolean wasAdapterEnabled = false; + private Permission cameraPermission = Permission.UNKNOWN; private Permission locationPermission = Permission.UNKNOWN; private BluetoothState bluetoothState = BluetoothState.UNKNOWN; @@ -96,14 +101,13 @@ public abstract class KeyAgreementActivity extends BriarActivity implements component.inject(this); } - @SuppressWarnings("ConstantConditions") @Override public void onCreate(@Nullable Bundle state) { super.onCreate(state); setContentView(R.layout.activity_fragment_container_toolbar); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); + requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true); if (state == null) { showInitialFragment(IntroFragment.newInstance()); } @@ -122,13 +126,11 @@ public abstract class KeyAgreementActivity extends BriarActivity implements @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - onBackPressed(); - return true; - default: - return super.onOptionsItemSelected(item); + if (item.getItemId() == android.R.id.home) { + onBackPressed(); + return true; } + return super.onOptionsItemSelected(item); } @Override @@ -206,7 +208,8 @@ public abstract class KeyAgreementActivity extends BriarActivity implements } @Override - public void onActivityResult(int request, int result, Intent data) { + public void onActivityResult(int request, int result, + @Nullable Intent data) { if (request == REQUEST_BLUETOOTH_DISCOVERABLE) { if (result == RESULT_CANCELED) { setBluetoothState(BluetoothState.REFUSED); @@ -239,12 +242,6 @@ public abstract class KeyAgreementActivity extends BriarActivity implements } } - protected void showErrorFragment(@StringRes int errorResId) { - String errorMsg = getString(errorResId); - BaseFragment f = ContactExchangeErrorFragment.newInstance(errorMsg); - showNextFragment(f); - } - private boolean checkPermissions() { if (areEssentialPermissionsGranted()) return true; // If the camera permission has been permanently denied, ask the From 413ce29c0c38befcce7c9998aa89e0ce43f58b8a Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 17 Jan 2020 10:32:01 +0000 Subject: [PATCH 29/69] Enable BT plugin before showing QR code. --- .../keyagreement/KeyAgreementActivity.java | 153 +++++++++++++++--- 1 file changed, 129 insertions(+), 24 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java index 67e125020..4f787598f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java @@ -8,10 +8,20 @@ import android.content.IntentFilter; import android.os.Bundle; import android.view.MenuItem; +import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.EventBus; +import org.briarproject.bramble.api.event.EventListener; +import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; +import org.briarproject.bramble.api.plugin.BluetoothConstants; +import org.briarproject.bramble.api.plugin.Plugin; +import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.event.BluetoothEnabledEvent; +import org.briarproject.bramble.api.plugin.event.TransportActiveEvent; +import org.briarproject.bramble.api.settings.Settings; +import org.briarproject.bramble.api.settings.SettingsManager; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BriarActivity; @@ -21,6 +31,7 @@ import org.briarproject.briar.android.keyagreement.IntroFragment.IntroScreenSeen import org.briarproject.briar.android.keyagreement.KeyAgreementFragment.KeyAgreementEventListener; import org.briarproject.briar.android.util.UiUtils; +import java.util.concurrent.Executor; import java.util.logging.Logger; import javax.annotation.Nullable; @@ -37,15 +48,15 @@ import static android.Manifest.permission.ACCESS_COARSE_LOCATION; import static android.Manifest.permission.CAMERA; import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE; import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED; -import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED; import static android.bluetooth.BluetoothAdapter.EXTRA_SCAN_MODE; -import static android.bluetooth.BluetoothAdapter.EXTRA_STATE; -import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE; import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE; -import static android.bluetooth.BluetoothAdapter.STATE_ON; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull; +import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENABLE; +import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; +import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_BLUETOOTH_DISCOVERABLE; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA_LOCATION; @@ -53,10 +64,41 @@ import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMI @ParametersNotNullByDefault public abstract class KeyAgreementActivity extends BriarActivity implements BaseFragmentListener, IntroScreenSeenListener, - KeyAgreementEventListener { + KeyAgreementEventListener, EventListener { private enum BluetoothState { - UNKNOWN, NO_ADAPTER, WAITING, REFUSED, ENABLED, DISCOVERABLE + /** + * We don't yet know the state of the Bluetooth adapter or plugin. + */ + UNKNOWN, + + /** + * The device doesn't have a Bluetooth adapter. + */ + NO_ADAPTER, + + /** + * We're waiting for the user to decide whether to allow + * discoverability, or the user has allowed discoverability and we're + * waiting for the adapter to become discoverable. + */ + WAITING, + + /** + * The user has refused discoverability. + */ + REFUSED, + + /** + * The Bluetooth adapter is discoverable. + */ + DISCOVERABLE, + + /** + * The Bluetooth adapter is discoverable and we're waiting for the + * Bluetooth plugin to become active. + */ + ACTIVATING_PLUGIN } private enum Permission { @@ -69,6 +111,16 @@ public abstract class KeyAgreementActivity extends BriarActivity implements @Inject EventBus eventBus; + @Inject + PluginManager pluginManager; + + @Inject + @IoExecutor + Executor ioExecutor; + + @Inject + SettingsManager settingsManager; + /** * Set to true in onPostResume() and false in onPause(). This prevents the * QR code fragment from being shown if onRequestPermissionsResult() is @@ -91,6 +143,13 @@ public abstract class KeyAgreementActivity extends BriarActivity implements */ private boolean wasAdapterEnabled = false; + /** + * Records whether the user accepted the most recent request (if any) to + * make the Bluetooth adapter discoverable, so we know whether to broadcast + * a {@link BluetoothEnabledEvent}. + */ + private boolean wasRequestAccepted = false; + private Permission cameraPermission = Permission.UNKNOWN; private Permission locationPermission = Permission.UNKNOWN; private BluetoothState bluetoothState = BluetoothState.UNKNOWN; @@ -111,9 +170,7 @@ public abstract class KeyAgreementActivity extends BriarActivity implements if (state == null) { showInitialFragment(IntroFragment.newInstance()); } - IntentFilter filter = new IntentFilter(); - filter.addAction(ACTION_STATE_CHANGED); - filter.addAction(ACTION_SCAN_MODE_CHANGED); + IntentFilter filter = new IntentFilter(ACTION_SCAN_MODE_CHANGED); bluetoothReceiver = new BluetoothStateReceiver(); registerReceiver(bluetoothReceiver, filter); } @@ -136,6 +193,7 @@ public abstract class KeyAgreementActivity extends BriarActivity implements @Override public void onStart() { super.onStart(); + eventBus.addListener(this); // Permissions may have been granted manually while we were stopped cameraPermission = Permission.UNKNOWN; locationPermission = Permission.UNKNOWN; @@ -152,11 +210,22 @@ public abstract class KeyAgreementActivity extends BriarActivity implements private void showQrCodeFragmentIfAllowed() { if (isResumed && continueClicked && areEssentialPermissionsGranted()) { - if (bluetoothState == BluetoothState.UNKNOWN || - bluetoothState == BluetoothState.ENABLED) { + if (bluetoothState == BluetoothState.UNKNOWN) { requestBluetoothDiscoverable(); - } else if (bluetoothState != BluetoothState.WAITING) { + } else if (bluetoothState == BluetoothState.NO_ADAPTER || + bluetoothState == BluetoothState.REFUSED) { + LOG.info("Continuing without Bluetooth"); showQrCodeFragment(); + } else if (bluetoothState == BluetoothState.DISCOVERABLE || + bluetoothState == BluetoothState.ACTIVATING_PLUGIN) { + if (isBluetoothPluginActive()) { + LOG.info("Bluetooth plugin is active"); + showQrCodeFragment(); + } else { + LOG.info("Enabling Bluetooth plugin"); + bluetoothState = BluetoothState.ACTIVATING_PLUGIN; + enableBluetoothPlugin(); + } } } } @@ -169,12 +238,36 @@ public abstract class KeyAgreementActivity extends BriarActivity implements locationPermission == Permission.PERMANENTLY_DENIED); } + private boolean isBluetoothPluginActive() { + Plugin p = pluginManager.getPlugin(BluetoothConstants.ID); + return p != null && p.getState() == ACTIVE; + } + + private void enableBluetoothPlugin() { + ioExecutor.execute(() -> { + try { + Settings s = new Settings(); + s.putBoolean(PREF_BT_ENABLE, true); + String namespace = BluetoothConstants.ID.getString(); + settingsManager.mergeSettings(s, namespace); + } catch (DbException e) { + logException(LOG, WARNING, e); + } + }); + } + @Override protected void onPause() { super.onPause(); isResumed = false; } + @Override + protected void onStop() { + super.onStop(); + eventBus.removeListener(this); + } + @Override public void showNextScreen() { continueClicked = true; @@ -200,8 +293,11 @@ public abstract class KeyAgreementActivity extends BriarActivity implements private void setBluetoothState(BluetoothState bluetoothState) { LOG.info("Setting Bluetooth state to " + bluetoothState); this.bluetoothState = bluetoothState; - if (!wasAdapterEnabled && bluetoothState == BluetoothState.ENABLED) { + if (!wasAdapterEnabled && wasRequestAccepted && + bluetoothState == BluetoothState.DISCOVERABLE) { + LOG.info("Bluetooth adapter was enabled by us"); eventBus.broadcast(new BluetoothEnabledEvent()); + wasRequestAccepted = false; wasAdapterEnabled = true; } showQrCodeFragmentIfAllowed(); @@ -214,12 +310,14 @@ public abstract class KeyAgreementActivity extends BriarActivity implements if (result == RESULT_CANCELED) { setBluetoothState(BluetoothState.REFUSED); } else { + wasRequestAccepted = true; // If Bluetooth is already discoverable, show the QR code - // otherwise wait for the state or scan mode to change BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter(); if (bt == null) throw new AssertionError(); - if (bt.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE) + if (bt.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE) { setBluetoothState(BluetoothState.DISCOVERABLE); + } } } else super.onActivityResult(request, result, data); } @@ -332,23 +430,30 @@ public abstract class KeyAgreementActivity extends BriarActivity implements permission); } + @Override + public void eventOccurred(Event e) { + if (e instanceof TransportActiveEvent) { + TransportActiveEvent t = (TransportActiveEvent) e; + if (t.getTransportId().equals(BluetoothConstants.ID)) { + showQrCodeFragmentIfAllowed(); + } + } + } + private class BluetoothStateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - if (ACTION_STATE_CHANGED.equals(action)) { - int state = intent.getIntExtra(EXTRA_STATE, 0); - if (state == STATE_ON) - setBluetoothState(BluetoothState.ENABLED); - else setBluetoothState(BluetoothState.UNKNOWN); - } else if (ACTION_SCAN_MODE_CHANGED.equals(action)) { + if (ACTION_SCAN_MODE_CHANGED.equals(action)) { int scanMode = intent.getIntExtra(EXTRA_SCAN_MODE, 0); - if (scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE) + if (scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE) { + LOG.info("Bluetooth adapter is discoverable"); setBluetoothState(BluetoothState.DISCOVERABLE); - else if (scanMode == SCAN_MODE_CONNECTABLE) - setBluetoothState(BluetoothState.ENABLED); - else setBluetoothState(BluetoothState.UNKNOWN); + } else if (bluetoothState == BluetoothState.DISCOVERABLE) { + LOG.info("Bluetooth adapter is not discoverable"); + setBluetoothState(BluetoothState.UNKNOWN); + } } } } From 1f94c2d4e8155af95ffd1dfcd84d90eae348d21a Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 17 Jan 2020 11:57:18 +0000 Subject: [PATCH 30/69] Enable LAN plugin in unit test. --- .../briarproject/bramble/plugin/tcp/LanTcpPluginTest.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bramble-core/src/test/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginTest.java b/bramble-core/src/test/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginTest.java index 0663df7b1..6596fd6a1 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginTest.java @@ -32,6 +32,7 @@ import static java.util.concurrent.Executors.newCachedThreadPool; import static java.util.concurrent.TimeUnit.SECONDS; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN; +import static org.briarproject.bramble.api.plugin.TcpConstants.PREF_TCP_ENABLE; import static org.briarproject.bramble.plugin.tcp.LanTcpPlugin.areAddressesInSameNetwork; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -303,10 +304,15 @@ public class LanTcpPluginTest extends BrambleTestCase { private final CountDownLatch propertiesLatch = new CountDownLatch(2); private final CountDownLatch connectionsLatch = new CountDownLatch(1); private final TransportProperties local = new TransportProperties(); + private final Settings settings = new Settings(); + + private Callback() { + settings.putBoolean(PREF_TCP_ENABLE, true); + } @Override public Settings getSettings() { - return new Settings(); + return settings; } @Override From a2de841e6a7df182963712b82b9b582b76c929ff Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 17 Jan 2020 13:11:54 +0000 Subject: [PATCH 31/69] Don't remove old settings yet. This avoids an unlikely race condition at startup, where the user opens the settings screen before the Tor plugin has migrated the settings. --- briar-android/src/main/res/values/arrays.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/briar-android/src/main/res/values/arrays.xml b/briar-android/src/main/res/values/arrays.xml index 8a7313b73..a9dc6aed6 100644 --- a/briar-android/src/main/res/values/arrays.xml +++ b/briar-android/src/main/res/values/arrays.xml @@ -4,11 +4,13 @@ @string/tor_network_setting_automatic @string/tor_network_setting_without_bridges @string/tor_network_setting_with_bridges + @string/tor_network_setting_never 0 1 2 + 3 From f5b4f6e071bf4eccec64d29cd7f25bb22c89b724 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 17 Jan 2020 13:12:16 +0000 Subject: [PATCH 32/69] Clean up logic for enabling/disabling settings. --- .../briar/android/settings/SettingsFragment.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java index 0cc96c6f8..8ddba892f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java @@ -573,10 +573,9 @@ public class SettingsFragment extends PreferenceFragmentCompat storeWifiSetting(wifiSetting); } else if (preference == enableTor) { boolean torEnabledSetting = (Boolean) newValue; - torNetwork.setEnabled(torNetwork.isEnabled() && torEnabledSetting); - torMobile.setEnabled(torMobile.isEnabled() && torEnabledSetting); - torOnlyWhenCharging.setEnabled(torOnlyWhenCharging.isEnabled() && - torEnabledSetting); + torNetwork.setEnabled(torEnabledSetting); + torMobile.setEnabled(torEnabledSetting); + torOnlyWhenCharging.setEnabled(torEnabledSetting); storeTorEnabledSetting(torEnabledSetting); } else if (preference == torNetwork) { int torNetworkSetting = Integer.valueOf((String) newValue); From 99686f5316a8265b376e099a0dec47f09ca8eb0a Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 20 Jan 2020 16:41:39 +0000 Subject: [PATCH 33/69] Use XML to specify dependencies between settings. --- .../briar/android/settings/SettingsFragment.java | 9 +++------ briar-android/src/main/res/xml/settings.xml | 3 +++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java index 8ddba892f..4e2ab8f83 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java @@ -464,9 +464,9 @@ public class SettingsFragment extends PreferenceFragmentCompat enableBluetooth.setEnabled(enabled); enableWifi.setEnabled(enabled); enableTor.setEnabled(enabled); - torNetwork.setEnabled(enabled && enableTor.isChecked()); - torMobile.setEnabled(enabled && enableTor.isChecked()); - torOnlyWhenCharging.setEnabled(enabled && enableTor.isChecked()); + torNetwork.setEnabled(enabled); + torMobile.setEnabled(enabled); + torOnlyWhenCharging.setEnabled(enabled); if (!enabled) screenLock.setEnabled(false); notifyPrivateMessages.setEnabled(enabled); notifyGroupMessages.setEnabled(enabled); @@ -573,9 +573,6 @@ public class SettingsFragment extends PreferenceFragmentCompat storeWifiSetting(wifiSetting); } else if (preference == enableTor) { boolean torEnabledSetting = (Boolean) newValue; - torNetwork.setEnabled(torEnabledSetting); - torMobile.setEnabled(torEnabledSetting); - torOnlyWhenCharging.setEnabled(torEnabledSetting); storeTorEnabledSetting(torEnabledSetting); } else if (preference == torNetwork) { int torNetworkSetting = Integer.valueOf((String) newValue); diff --git a/briar-android/src/main/res/xml/settings.xml b/briar-android/src/main/res/xml/settings.xml index b1b40552f..db50e4732 100644 --- a/briar-android/src/main/res/xml/settings.xml +++ b/briar-android/src/main/res/xml/settings.xml @@ -55,6 +55,7 @@ Date: Thu, 23 Jan 2020 12:51:41 +0000 Subject: [PATCH 34/69] Remove redundant call to pluginStateChanged(). --- .../main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java | 1 - 1 file changed, 1 deletion(-) diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java index b5bd7cb4e..c7cbd0ba9 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java @@ -391,7 +391,6 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { boolean enabledByUser = settings.getBoolean(PREF_TCP_ENABLE, false); ServerSocket ss = state.setEnabledByUser(enabledByUser); State s = getState(); - callback.pluginStateChanged(s); if (ss != null) { LOG.info("Disabled by user, closing server socket"); tryToClose(ss, LOG, WARNING); From c783a2f3525aeece00cfda5886fa093ab6c47a30 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 23 Jan 2020 13:18:38 +0000 Subject: [PATCH 35/69] Enable LAN plugin before showing QR code. --- .../keyagreement/KeyAgreementActivity.java | 226 ++++++++++-------- 1 file changed, 131 insertions(+), 95 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java index 4f787598f..771c17cdc 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java @@ -16,10 +16,13 @@ import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.plugin.BluetoothConstants; +import org.briarproject.bramble.api.plugin.LanTcpConstants; import org.briarproject.bramble.api.plugin.Plugin; +import org.briarproject.bramble.api.plugin.Plugin.State; import org.briarproject.bramble.api.plugin.PluginManager; +import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.event.BluetoothEnabledEvent; -import org.briarproject.bramble.api.plugin.event.TransportActiveEvent; +import org.briarproject.bramble.api.plugin.event.TransportStateEvent; import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.SettingsManager; import org.briarproject.briar.R; @@ -48,14 +51,17 @@ import static android.Manifest.permission.ACCESS_COARSE_LOCATION; import static android.Manifest.permission.CAMERA; import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE; import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED; -import static android.bluetooth.BluetoothAdapter.EXTRA_SCAN_MODE; import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENABLE; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; +import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; +import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; +import static org.briarproject.bramble.api.plugin.TcpConstants.PREF_TCP_ENABLE; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_BLUETOOTH_DISCOVERABLE; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA_LOCATION; @@ -66,9 +72,9 @@ public abstract class KeyAgreementActivity extends BriarActivity implements BaseFragmentListener, IntroScreenSeenListener, KeyAgreementEventListener, EventListener { - private enum BluetoothState { + private enum BluetoothDecision { /** - * We don't yet know the state of the Bluetooth adapter or plugin. + * We haven't asked the user about Bluetooth discoverability. */ UNKNOWN, @@ -78,27 +84,19 @@ public abstract class KeyAgreementActivity extends BriarActivity implements NO_ADAPTER, /** - * We're waiting for the user to decide whether to allow - * discoverability, or the user has allowed discoverability and we're - * waiting for the adapter to become discoverable. + * We're waiting for the user to accept or refuse discoverability. */ WAITING, + /** + * The user has accepted discoverability. + */ + ACCEPTED, + /** * The user has refused discoverability. */ - REFUSED, - - /** - * The Bluetooth adapter is discoverable. - */ - DISCOVERABLE, - - /** - * The Bluetooth adapter is discoverable and we're waiting for the - * Bluetooth plugin to become active. - */ - ACTIVATING_PLUGIN + REFUSED } private enum Permission { @@ -144,15 +142,20 @@ public abstract class KeyAgreementActivity extends BriarActivity implements private boolean wasAdapterEnabled = false; /** - * Records whether the user accepted the most recent request (if any) to - * make the Bluetooth adapter discoverable, so we know whether to broadcast - * a {@link BluetoothEnabledEvent}. + * Records whether we've enabled the wifi plugin so we don't enable it more + * than once. */ - private boolean wasRequestAccepted = false; + private boolean hasEnabledWifi = false; + + /** + * Records whether we've enabled the Bluetooth plugin so we don't enable it + * more than once. + */ + private boolean hasEnabledBluetooth = false; private Permission cameraPermission = Permission.UNKNOWN; private Permission locationPermission = Permission.UNKNOWN; - private BluetoothState bluetoothState = BluetoothState.UNKNOWN; + private BluetoothDecision bluetoothDecision = BluetoothDecision.UNKNOWN; private BroadcastReceiver bluetoothReceiver = null; @Override @@ -210,21 +213,21 @@ public abstract class KeyAgreementActivity extends BriarActivity implements private void showQrCodeFragmentIfAllowed() { if (isResumed && continueClicked && areEssentialPermissionsGranted()) { - if (bluetoothState == BluetoothState.UNKNOWN) { - requestBluetoothDiscoverable(); - } else if (bluetoothState == BluetoothState.NO_ADAPTER || - bluetoothState == BluetoothState.REFUSED) { - LOG.info("Continuing without Bluetooth"); + if (isWifiReady() && isBluetoothReady()) { + LOG.info("Wifi and Bluetooth are ready"); showQrCodeFragment(); - } else if (bluetoothState == BluetoothState.DISCOVERABLE || - bluetoothState == BluetoothState.ACTIVATING_PLUGIN) { - if (isBluetoothPluginActive()) { - LOG.info("Bluetooth plugin is active"); - showQrCodeFragment(); - } else { + } else { + if (shouldEnableWifi()) { + LOG.info("Enabling wifi plugin"); + hasEnabledWifi = true; + enablePlugin(LanTcpConstants.ID, PREF_TCP_ENABLE); + } + if (bluetoothDecision == BluetoothDecision.UNKNOWN) { + requestBluetoothDiscoverable(); + } else if (shouldEnableBluetooth()) { LOG.info("Enabling Bluetooth plugin"); - bluetoothState = BluetoothState.ACTIVATING_PLUGIN; - enableBluetoothPlugin(); + hasEnabledBluetooth = true; + enablePlugin(BluetoothConstants.ID, PREF_BT_ENABLE); } } } @@ -238,24 +241,81 @@ public abstract class KeyAgreementActivity extends BriarActivity implements locationPermission == Permission.PERMANENTLY_DENIED); } - private boolean isBluetoothPluginActive() { - Plugin p = pluginManager.getPlugin(BluetoothConstants.ID); - return p != null && p.getState() == ACTIVE; + private boolean isWifiReady() { + Plugin p = pluginManager.getPlugin(LanTcpConstants.ID); + if (p == null) return true; // Continue without wifi + State state = p.getState(); + // Wait for plugin to become enabled + return state == ACTIVE || state == INACTIVE; } - private void enableBluetoothPlugin() { + private boolean isBluetoothReady() { + if (bluetoothDecision == BluetoothDecision.UNKNOWN || + bluetoothDecision == BluetoothDecision.WAITING) { + // Wait for decision + return false; + } + if (bluetoothDecision == BluetoothDecision.NO_ADAPTER + || bluetoothDecision == BluetoothDecision.REFUSED) { + // Continue without Bluetooth + return true; + } + BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter(); + if (bt == null) return true; // Continue without Bluetooth + if (bt.getScanMode() != SCAN_MODE_CONNECTABLE_DISCOVERABLE) { + // Wait for adapter to become discoverable + return false; + } + Plugin p = pluginManager.getPlugin(BluetoothConstants.ID); + if (p == null) return true; // Continue without Bluetooth + // Wait for plugin to become active + return p.getState() == ACTIVE; + } + + private boolean shouldEnableWifi() { + if (hasEnabledWifi) return false; + Plugin p = pluginManager.getPlugin(LanTcpConstants.ID); + return p != null && p.getState() == DISABLED; + } + + private void enablePlugin(TransportId t, String settingKey) { ioExecutor.execute(() -> { try { Settings s = new Settings(); - s.putBoolean(PREF_BT_ENABLE, true); - String namespace = BluetoothConstants.ID.getString(); - settingsManager.mergeSettings(s, namespace); + s.putBoolean(settingKey, true); + settingsManager.mergeSettings(s, t.getString()); } catch (DbException e) { logException(LOG, WARNING, e); } }); } + private void requestBluetoothDiscoverable() { + BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter(); + if (bt == null) { + bluetoothDecision = BluetoothDecision.NO_ADAPTER; + showQrCodeFragmentIfAllowed(); + } else { + Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE); + if (i.resolveActivity(getPackageManager()) != null) { + LOG.info("Asking for Bluetooth discoverability"); + bluetoothDecision = BluetoothDecision.WAITING; + wasAdapterEnabled = bt.isEnabled(); + startActivityForResult(i, REQUEST_BLUETOOTH_DISCOVERABLE); + } else { + bluetoothDecision = BluetoothDecision.NO_ADAPTER; + showQrCodeFragmentIfAllowed(); + } + } + } + + private boolean shouldEnableBluetooth() { + if (bluetoothDecision != BluetoothDecision.ACCEPTED) return false; + if (hasEnabledBluetooth) return false; + Plugin p = pluginManager.getPlugin(BluetoothConstants.ID); + return p != null && p.getState() == DISABLED; + } + @Override protected void onPause() { super.onPause(); @@ -274,51 +334,23 @@ public abstract class KeyAgreementActivity extends BriarActivity implements if (checkPermissions()) showQrCodeFragmentIfAllowed(); } - private void requestBluetoothDiscoverable() { - BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter(); - if (bt == null) { - setBluetoothState(BluetoothState.NO_ADAPTER); - } else { - Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE); - if (i.resolveActivity(getPackageManager()) != null) { - setBluetoothState(BluetoothState.WAITING); - wasAdapterEnabled = bt.isEnabled(); - startActivityForResult(i, REQUEST_BLUETOOTH_DISCOVERABLE); - } else { - setBluetoothState(BluetoothState.NO_ADAPTER); - } - } - } - - private void setBluetoothState(BluetoothState bluetoothState) { - LOG.info("Setting Bluetooth state to " + bluetoothState); - this.bluetoothState = bluetoothState; - if (!wasAdapterEnabled && wasRequestAccepted && - bluetoothState == BluetoothState.DISCOVERABLE) { - LOG.info("Bluetooth adapter was enabled by us"); - eventBus.broadcast(new BluetoothEnabledEvent()); - wasRequestAccepted = false; - wasAdapterEnabled = true; - } - showQrCodeFragmentIfAllowed(); - } - @Override public void onActivityResult(int request, int result, @Nullable Intent data) { if (request == REQUEST_BLUETOOTH_DISCOVERABLE) { if (result == RESULT_CANCELED) { - setBluetoothState(BluetoothState.REFUSED); + LOG.info("Bluetooth discoverability was refused"); + bluetoothDecision = BluetoothDecision.REFUSED; } else { - wasRequestAccepted = true; - // If Bluetooth is already discoverable, show the QR code - - // otherwise wait for the state or scan mode to change - BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter(); - if (bt == null) throw new AssertionError(); - if (bt.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE) { - setBluetoothState(BluetoothState.DISCOVERABLE); + LOG.info("Bluetooth discoverability was accepted"); + bluetoothDecision = BluetoothDecision.ACCEPTED; + if (!wasAdapterEnabled) { + LOG.info("Bluetooth adapter was enabled by us"); + eventBus.broadcast(new BluetoothEnabledEvent()); + wasAdapterEnabled = true; } } + showQrCodeFragmentIfAllowed(); } else super.onActivityResult(request, result, data); } @@ -328,7 +360,12 @@ public abstract class KeyAgreementActivity extends BriarActivity implements continueClicked = false; // If we return to the intro fragment, ask for Bluetooth // discoverability again before showing the QR code fragment - bluetoothState = BluetoothState.UNKNOWN; + bluetoothDecision = BluetoothDecision.UNKNOWN; + // If we return to the intro fragment, we may need to enable wifi and + // Bluetooth again + hasEnabledWifi = false; + hasEnabledBluetooth = false; + // FIXME #824 FragmentManager fm = getSupportFragmentManager(); if (fm.findFragmentByTag(KeyAgreementFragment.TAG) == null) { @@ -432,9 +469,17 @@ public abstract class KeyAgreementActivity extends BriarActivity implements @Override public void eventOccurred(Event e) { - if (e instanceof TransportActiveEvent) { - TransportActiveEvent t = (TransportActiveEvent) e; + if (e instanceof TransportStateEvent) { + TransportStateEvent t = (TransportStateEvent) e; if (t.getTransportId().equals(BluetoothConstants.ID)) { + if (LOG.isLoggable(INFO)) { + LOG.info("Bluetooth state changed to " + t.getState()); + } + showQrCodeFragmentIfAllowed(); + } else if (t.getTransportId().equals(LanTcpConstants.ID)) { + if (LOG.isLoggable(INFO)) { + LOG.info("Wifi state changed to " + t.getState()); + } showQrCodeFragmentIfAllowed(); } } @@ -444,17 +489,8 @@ public abstract class KeyAgreementActivity extends BriarActivity implements @Override public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (ACTION_SCAN_MODE_CHANGED.equals(action)) { - int scanMode = intent.getIntExtra(EXTRA_SCAN_MODE, 0); - if (scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE) { - LOG.info("Bluetooth adapter is discoverable"); - setBluetoothState(BluetoothState.DISCOVERABLE); - } else if (bluetoothState == BluetoothState.DISCOVERABLE) { - LOG.info("Bluetooth adapter is not discoverable"); - setBluetoothState(BluetoothState.UNKNOWN); - } - } + LOG.info("Bluetooth scan mode changed"); + showQrCodeFragmentIfAllowed(); } } } From ee74b3774b82591114c77b7c66c5fb4f93542493 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 23 Jan 2020 13:24:37 +0000 Subject: [PATCH 36/69] Remove another redundant call to pluginStateChanged(). --- .../briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java | 1 - 1 file changed, 1 deletion(-) diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java index 111ea7894..b90e42fe6 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java @@ -437,7 +437,6 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { boolean enabledByUser = settings.getBoolean(PREF_BT_ENABLE, false); SS ss = state.setEnabledByUser(enabledByUser); State s = getState(); - callback.pluginStateChanged(s); if (ss != null) { LOG.info("Disabled by user, closing server socket"); tryToClose(ss); From 0fec5d778346473eef9f4e5da1148650f37b7a60 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Fri, 24 Jan 2020 09:17:02 -0300 Subject: [PATCH 37/69] [bramble] Add method for enabling/disabling plugins to PluginManager --- .../plugin/tcp/AndroidLanTcpPlugin.java | 3 +- .../api/plugin/BluetoothConstants.java | 2 -- .../bramble/api/plugin/Plugin.java | 7 ++++ .../bramble/api/plugin/PluginManager.java | 13 ++++++++ .../bramble/api/plugin/TcpConstants.java | 6 ---- .../bramble/api/plugin/TorConstants.java | 1 - .../bramble/plugin/PluginManagerImpl.java | 27 ++++++++++++--- .../plugin/bluetooth/BluetoothPlugin.java | 5 ++- .../bramble/plugin/tcp/TcpPlugin.java | 5 ++- .../bramble/plugin/tor/TorPlugin.java | 5 ++- .../bramble/plugin/tcp/LanTcpPluginTest.java | 4 +-- .../keyagreement/KeyAgreementActivity.java | 33 ++----------------- .../android/settings/SettingsFragment.java | 16 ++++----- 13 files changed, 60 insertions(+), 67 deletions(-) delete mode 100644 bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TcpConstants.java 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 ffbeab9d3..c3d080918 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 @@ -36,7 +36,6 @@ import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; -import static org.briarproject.bramble.api.plugin.TcpConstants.PREF_TCP_ENABLE; import static org.briarproject.bramble.util.IoUtils.tryToClose; @NotNullByDefault @@ -74,7 +73,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { if (used.getAndSet(true)) throw new IllegalStateException(); initialisePortProperty(); Settings settings = callback.getSettings(); - state.setStarted(settings.getBoolean(PREF_TCP_ENABLE, false)); + state.setStarted(settings.getBoolean(PREF_PLUGIN_ENABLE, false)); updateConnectionStatus(); } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java index 915dfbbab..1752ba10f 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java @@ -9,8 +9,6 @@ public interface BluetoothConstants { String PROP_ADDRESS = "address"; String PROP_UUID = "uuid"; - String PREF_BT_ENABLE = "enable"; - // Reason code returned by Plugin#getReasonDisabled() int REASON_NO_BT_ADAPTER = 2; } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java index ee26500f1..4ecd851e2 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java @@ -3,6 +3,7 @@ package org.briarproject.bramble.api.plugin; import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.properties.TransportProperties; +import org.briarproject.bramble.api.settings.SettingsManager; import java.util.Collection; @@ -34,6 +35,12 @@ public interface Plugin { INACTIVE } + /** + * The string for the boolean preference + * to use with the {@link SettingsManager} to enable or disable the plugin. + */ + String PREF_PLUGIN_ENABLE = "enable"; + /** * Reason code returned by {@link #getReasonDisabled()} to indicate that * the plugin is disabled because it has not been started or has been diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/PluginManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/PluginManager.java index 9c34b434d..5132ba3c6 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/PluginManager.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/PluginManager.java @@ -41,4 +41,17 @@ public interface PluginManager { * Returns any duplex plugins that support rendezvous. */ Collection getRendezvousPlugins(); + + /** + * Enables or disables the plugin + * identified by the given {@link TransportId}. + *

+ * Note that this applies the change asynchronously + * and there are no order guarantees. + *

+ * If no plugin with the given {@link TransportId} is registered, + * this is a no-op. + */ + void setPluginEnabled(TransportId t, boolean enabled); + } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TcpConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TcpConstants.java deleted file mode 100644 index 503f2cb88..000000000 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TcpConstants.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.briarproject.bramble.api.plugin; - -public interface TcpConstants { - - String PREF_TCP_ENABLE = "enable"; -} diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java index 727dab4b4..a9ae3c5ae 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java @@ -13,7 +13,6 @@ public interface TorConstants { int CONNECT_TO_PROXY_TIMEOUT = 5000; // Milliseconds int EXTRA_SOCKET_TIMEOUT = 30000; // Milliseconds - String PREF_TOR_ENABLE = "enable"; String PREF_TOR_NETWORK = "network2"; String PREF_TOR_PORT = "port"; String PREF_TOR_MOBILE = "useMobileData"; diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java index 064b12856..fe0f4ebfb 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java @@ -48,6 +48,7 @@ import static java.util.logging.Level.FINE; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; +import static org.briarproject.bramble.api.plugin.Plugin.PREF_PLUGIN_ENABLE; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; import static org.briarproject.bramble.util.LogUtils.logDuration; @@ -182,6 +183,26 @@ class PluginManagerImpl implements PluginManager, Service { return supported; } + @Override + public void setPluginEnabled(TransportId t, boolean enabled) { + Plugin plugin = plugins.get(t); + if (plugin == null) return; + + Settings s = new Settings(); + s.putBoolean(PREF_PLUGIN_ENABLE, enabled); + ioExecutor.execute(() -> mergeSettings(s, t.getString())); + } + + private void mergeSettings(Settings s, String namespace) { + try { + long start = now(); + settingsManager.mergeSettings(s, namespace); + logDuration(LOG, "Merging settings", start); + } catch (DbException e) { + logException(LOG, WARNING, e); + } + } + private class PluginStarter implements Runnable { private final Plugin plugin; @@ -284,11 +305,7 @@ class PluginManagerImpl implements PluginManager, Service { @Override public void mergeSettings(Settings s) { - try { - settingsManager.mergeSettings(s, id.getString()); - } catch (DbException e) { - logException(LOG, WARNING, e); - } + PluginManagerImpl.this.mergeSettings(s, id.getString()); } @Override diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java index b90e42fe6..4c1133762 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java @@ -47,7 +47,6 @@ import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH; import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID; -import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENABLE; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID; import static org.briarproject.bramble.api.plugin.BluetoothConstants.REASON_NO_BT_ADAPTER; @@ -172,7 +171,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { } updateProperties(); Settings settings = callback.getSettings(); - boolean enabledByUser = settings.getBoolean(PREF_BT_ENABLE, false); + boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE, false); state.setStarted(enabledByUser); if (enabledByUser) { if (isAdapterEnabled()) bind(); @@ -434,7 +433,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { @IoExecutor private void onSettingsUpdated(Settings settings) { - boolean enabledByUser = settings.getBoolean(PREF_BT_ENABLE, false); + boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE, false); SS ss = state.setEnabledByUser(enabledByUser); State s = getState(); if (ss != null) { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java index c7cbd0ba9..69b860088 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java @@ -51,7 +51,6 @@ import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; -import static org.briarproject.bramble.api.plugin.TcpConstants.PREF_TCP_ENABLE; import static org.briarproject.bramble.util.IoUtils.tryToClose; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress; @@ -129,7 +128,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { public void start() { if (used.getAndSet(true)) throw new IllegalStateException(); Settings settings = callback.getSettings(); - state.setStarted(settings.getBoolean(PREF_TCP_ENABLE, false)); + state.setStarted(settings.getBoolean(PREF_PLUGIN_ENABLE, false)); bind(); } @@ -388,7 +387,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { @IoExecutor private void onSettingsUpdated(Settings settings) { - boolean enabledByUser = settings.getBoolean(PREF_TCP_ENABLE, false); + boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE, false); ServerSocket ss = state.setEnabledByUser(enabledByUser); State s = getState(); if (ss != null) { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index afdeb6dd6..db69469f1 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -75,7 +75,6 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; import static org.briarproject.bramble.api.plugin.TorConstants.CONTROL_PORT; import static org.briarproject.bramble.api.plugin.TorConstants.ID; -import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ENABLE; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_AUTOMATIC; @@ -294,7 +293,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { PREF_TOR_NETWORK_AUTOMATIC); if (network == PREF_TOR_NETWORK_NEVER) { settings.putInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_AUTOMATIC); - settings.putBoolean(PREF_TOR_ENABLE, false); + settings.putBoolean(PREF_PLUGIN_ENABLE, false); callback.mergeSettings(settings); } return settings; @@ -775,7 +774,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { String country = locationUtils.getCurrentCountry(); boolean blocked = circumventionProvider.isTorProbablyBlocked(country); - boolean enabledByUser = settings.getBoolean(PREF_TOR_ENABLE, true); + boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE, true); int network = settings.getInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_AUTOMATIC); boolean useMobile = settings.getBoolean(PREF_TOR_MOBILE, true); diff --git a/bramble-core/src/test/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginTest.java b/bramble-core/src/test/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginTest.java index 6596fd6a1..1ae844f7b 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginTest.java @@ -32,7 +32,7 @@ import static java.util.concurrent.Executors.newCachedThreadPool; import static java.util.concurrent.TimeUnit.SECONDS; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN; -import static org.briarproject.bramble.api.plugin.TcpConstants.PREF_TCP_ENABLE; +import static org.briarproject.bramble.api.plugin.Plugin.PREF_PLUGIN_ENABLE; import static org.briarproject.bramble.plugin.tcp.LanTcpPlugin.areAddressesInSameNetwork; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -307,7 +307,7 @@ public class LanTcpPluginTest extends BrambleTestCase { private final Settings settings = new Settings(); private Callback() { - settings.putBoolean(PREF_TCP_ENABLE, true); + settings.putBoolean(PREF_PLUGIN_ENABLE, true); } @Override diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java index 771c17cdc..72e7b69c8 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java @@ -8,11 +8,9 @@ import android.content.IntentFilter; import android.os.Bundle; import android.view.MenuItem; -import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventListener; -import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.plugin.BluetoothConstants; @@ -20,11 +18,8 @@ import org.briarproject.bramble.api.plugin.LanTcpConstants; import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin.State; import org.briarproject.bramble.api.plugin.PluginManager; -import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.event.BluetoothEnabledEvent; import org.briarproject.bramble.api.plugin.event.TransportStateEvent; -import org.briarproject.bramble.api.settings.Settings; -import org.briarproject.bramble.api.settings.SettingsManager; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BriarActivity; @@ -34,7 +29,6 @@ import org.briarproject.briar.android.keyagreement.IntroFragment.IntroScreenSeen import org.briarproject.briar.android.keyagreement.KeyAgreementFragment.KeyAgreementEventListener; import org.briarproject.briar.android.util.UiUtils; -import java.util.concurrent.Executor; import java.util.logging.Logger; import javax.annotation.Nullable; @@ -54,15 +48,11 @@ import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED; import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static java.util.logging.Level.INFO; -import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull; -import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENABLE; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; -import static org.briarproject.bramble.api.plugin.TcpConstants.PREF_TCP_ENABLE; -import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_BLUETOOTH_DISCOVERABLE; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA_LOCATION; @@ -112,13 +102,6 @@ public abstract class KeyAgreementActivity extends BriarActivity implements @Inject PluginManager pluginManager; - @Inject - @IoExecutor - Executor ioExecutor; - - @Inject - SettingsManager settingsManager; - /** * Set to true in onPostResume() and false in onPause(). This prevents the * QR code fragment from being shown if onRequestPermissionsResult() is @@ -220,14 +203,14 @@ public abstract class KeyAgreementActivity extends BriarActivity implements if (shouldEnableWifi()) { LOG.info("Enabling wifi plugin"); hasEnabledWifi = true; - enablePlugin(LanTcpConstants.ID, PREF_TCP_ENABLE); + pluginManager.setPluginEnabled(LanTcpConstants.ID, true); } if (bluetoothDecision == BluetoothDecision.UNKNOWN) { requestBluetoothDiscoverable(); } else if (shouldEnableBluetooth()) { LOG.info("Enabling Bluetooth plugin"); hasEnabledBluetooth = true; - enablePlugin(BluetoothConstants.ID, PREF_BT_ENABLE); + pluginManager.setPluginEnabled(BluetoothConstants.ID, true); } } } @@ -278,18 +261,6 @@ public abstract class KeyAgreementActivity extends BriarActivity implements return p != null && p.getState() == DISABLED; } - private void enablePlugin(TransportId t, String settingKey) { - ioExecutor.execute(() -> { - try { - Settings s = new Settings(); - s.putBoolean(settingKey, true); - settingsManager.mergeSettings(s, t.getString()); - } catch (DbException e) { - logException(LOG, WARNING, e); - } - }); - } - private void requestBluetoothDiscoverable() { BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter(); if (bt == null) { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java index 4e2ab8f83..5d284376c 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java @@ -73,9 +73,7 @@ import static android.widget.Toast.LENGTH_SHORT; import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_LTR; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; -import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENABLE; -import static org.briarproject.bramble.api.plugin.TcpConstants.PREF_TCP_ENABLE; -import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ENABLE; +import static org.briarproject.bramble.api.plugin.Plugin.PREF_PLUGIN_ENABLE; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_AUTOMATIC; @@ -379,15 +377,15 @@ public class SettingsFragment extends PreferenceFragmentCompat if (!settingsLoaded) return; boolean btEnabledSetting = - btSettings.getBoolean(PREF_BT_ENABLE, false); + btSettings.getBoolean(PREF_PLUGIN_ENABLE, false); enableBluetooth.setChecked(btEnabledSetting); boolean wifiEnabledSetting = - wifiSettings.getBoolean(PREF_TCP_ENABLE, false); + wifiSettings.getBoolean(PREF_PLUGIN_ENABLE, false); enableWifi.setChecked(wifiEnabledSetting); boolean torEnabledSetting = - torSettings.getBoolean(PREF_TOR_ENABLE, true); + torSettings.getBoolean(PREF_PLUGIN_ENABLE, true); enableTor.setChecked(torEnabledSetting); int torNetworkSetting = torSettings.getInt(PREF_TOR_NETWORK, @@ -639,7 +637,7 @@ public class SettingsFragment extends PreferenceFragmentCompat private void storeTorEnabledSetting(boolean torEnabledSetting) { Settings s = new Settings(); - s.putBoolean(PREF_TOR_ENABLE, torEnabledSetting); + s.putBoolean(PREF_PLUGIN_ENABLE, torEnabledSetting); mergeSettings(s, TOR_NAMESPACE); } @@ -663,13 +661,13 @@ public class SettingsFragment extends PreferenceFragmentCompat private void storeBluetoothSetting(boolean btSetting) { Settings s = new Settings(); - s.putBoolean(PREF_BT_ENABLE, btSetting); + s.putBoolean(PREF_PLUGIN_ENABLE, btSetting); mergeSettings(s, BT_NAMESPACE); } private void storeWifiSetting(boolean wifiSetting) { Settings s = new Settings(); - s.putBoolean(PREF_TCP_ENABLE, wifiSetting); + s.putBoolean(PREF_PLUGIN_ENABLE, wifiSetting); mergeSettings(s, WIFI_NAMESPACE); } From 96e32ad64efb0a7e5f9af295c4041f5f72e48c67 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Wed, 15 Jan 2020 10:48:03 -0300 Subject: [PATCH 38/69] [android] Add transport plugin toggles to NavDrawer --- .../artwork/transports_list_scene.xml | 117 +++++++++++++ .../android/navdrawer/NavDrawerActivity.java | 145 +++------------ .../res/drawable/ic_baseline_drag_handle.xml | 12 ++ .../src/main/res/layout/navigation_menu.xml | 28 +-- .../src/main/res/layout/transports_list.xml | 165 ++++++++++++++++-- .../src/main/res/menu/navigation_drawer.xml | 7 +- briar-android/src/main/res/values/strings.xml | 5 +- 7 files changed, 319 insertions(+), 160 deletions(-) create mode 100644 briar-android/artwork/transports_list_scene.xml create mode 100644 briar-android/src/main/res/drawable/ic_baseline_drag_handle.xml diff --git a/briar-android/artwork/transports_list_scene.xml b/briar-android/artwork/transports_list_scene.xml new file mode 100644 index 000000000..bcb0d82ea --- /dev/null +++ b/briar-android/artwork/transports_list_scene.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java index 3c63d1c4e..e80aec526 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java @@ -1,17 +1,15 @@ package org.briarproject.briar.android.navdrawer; -import android.annotation.SuppressLint; import android.content.Intent; import android.content.res.Configuration; import android.net.Uri; import android.os.Bundle; -import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.GridView; +import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.ImageView; +import android.widget.ScrollView; import android.widget.TextView; import com.google.android.material.navigation.NavigationView; @@ -21,10 +19,7 @@ import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; -import org.briarproject.bramble.api.plugin.BluetoothConstants; -import org.briarproject.bramble.api.plugin.LanTcpConstants; import org.briarproject.bramble.api.plugin.Plugin.State; -import org.briarproject.bramble.api.plugin.TorConstants; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; @@ -39,25 +34,22 @@ import org.briarproject.briar.android.logout.SignOutFragment; import org.briarproject.briar.android.privategroup.list.GroupListFragment; import org.briarproject.briar.android.settings.SettingsActivity; -import java.util.ArrayList; -import java.util.List; import java.util.logging.Logger; import javax.inject.Inject; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.UiThread; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.widget.Toolbar; import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import static android.view.View.GONE; +import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; import static androidx.core.view.GravityCompat.START; import static androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED; @@ -65,8 +57,6 @@ import static androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE; import static java.util.Objects.requireNonNull; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING; -import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; -import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING; import static org.briarproject.briar.android.BriarService.EXTRA_STARTUP_FAILED; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD; import static org.briarproject.briar.android.navdrawer.IntentRouter.handleExternalIntent; @@ -102,11 +92,9 @@ public class NavDrawerActivity extends BriarActivity implements LifecycleManager lifecycleManager; private DrawerLayout drawerLayout; + private ScrollView drawerScrollView; private NavigationView navigation; - private List transports; - private BaseAdapter transportsAdapter; - @Override public void injectActivity(ActivityComponent component) { component.inject(this); @@ -118,10 +106,26 @@ public class NavDrawerActivity extends BriarActivity implements exitIfStartupFailed(getIntent()); setContentView(R.layout.activity_nav_drawer); + drawerScrollView = findViewById(R.id.drawerScrollView); + drawerScrollView.getViewTreeObserver().addOnGlobalLayoutListener( + new OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + // hide/show chevron depending on whether we can scroll + View chevronView = findViewById(R.id.chevronView); + if (drawerScrollView.canScrollVertically(1)) { + chevronView.setVisibility(VISIBLE); + } else { + chevronView.setVisibility(INVISIBLE); + } + drawerScrollView.getViewTreeObserver() + .removeOnGlobalLayoutListener(this); + } + }); + Toolbar toolbar = findViewById(R.id.toolbar); drawerLayout = findViewById(R.id.drawer_layout); navigation = findViewById(R.id.navigation); - GridView transportsView = findViewById(R.id.transportsView); setSupportActionBar(toolbar); ActionBar actionBar = requireNonNull(getSupportActionBar()); @@ -134,9 +138,6 @@ public class NavDrawerActivity extends BriarActivity implements drawerLayout.addDrawerListener(drawerToggle); navigation.setNavigationItemSelectedListener(this); - initializeTransports(getLayoutInflater()); - transportsView.setAdapter(transportsAdapter); - lockManager.isLockable().observe(this, this::setLockVisible); if (lifecycleManager.getLifecycleState().isAfter(RUNNING)) { @@ -152,10 +153,8 @@ public class NavDrawerActivity extends BriarActivity implements } @Override - @SuppressLint("NewApi") public void onStart() { super.onStart(); - updateTransports(); lockManager.checkIfLockable(); controller.showExpiryWarning(new UiResultHandler(this) { @Override @@ -373,109 +372,9 @@ public class NavDrawerActivity extends BriarActivity implements expiryWarning.setVisibility(VISIBLE); } - private void initializeTransports(LayoutInflater inflater) { - transports = new ArrayList<>(3); - - Transport tor = new Transport(); - tor.id = TorConstants.ID; - tor.state = controller.getTransportState(tor.id); - tor.iconId = R.drawable.transport_tor; - tor.textId = R.string.transport_tor; - transports.add(tor); - - Transport bt = new Transport(); - bt.id = BluetoothConstants.ID; - bt.state = controller.getTransportState(bt.id); - bt.iconId = R.drawable.transport_bt; - bt.textId = R.string.transport_bt; - transports.add(bt); - - Transport lan = new Transport(); - lan.id = LanTcpConstants.ID; - lan.state = controller.getTransportState(lan.id); - lan.iconId = R.drawable.transport_lan; - lan.textId = R.string.transport_lan; - transports.add(lan); - - transportsAdapter = new BaseAdapter() { - @Override - public int getCount() { - return transports.size(); - } - - @Override - public Transport getItem(int position) { - return transports.get(position); - } - - @Override - public long getItemId(int position) { - return 0; - } - - @Override - public View getView(int position, View convertView, - ViewGroup parent) { - View view; - if (convertView != null) { - view = convertView; - } else { - view = inflater.inflate(R.layout.list_item_transport, - parent, false); - } - - Transport t = getItem(position); - int c; - if (t.state == ACTIVE) { - c = ContextCompat.getColor(NavDrawerActivity.this, - R.color.briar_green_light); - } else if (t.state == ENABLING) { - c = ContextCompat.getColor(NavDrawerActivity.this, - R.color.briar_yellow); - } else { - c = ContextCompat.getColor(NavDrawerActivity.this, - android.R.color.tertiary_text_light); - } - - ImageView icon = view.findViewById(R.id.imageView); - icon.setImageDrawable(ContextCompat - .getDrawable(NavDrawerActivity.this, t.iconId)); - icon.setColorFilter(c); - - TextView text = view.findViewById(R.id.textView); - text.setText(getString(t.textId)); - - return view; - } - }; - } - - private void updateTransports() { - if (transports == null || transportsAdapter == null) return; - for (Transport t : transports) { - t.state = controller.getTransportState(t.id); - } - transportsAdapter.notifyDataSetChanged(); - } - @Override public void stateUpdate(TransportId id, State state) { - if (transports == null || transportsAdapter == null) return; - for (Transport t : transports) { - if (t.id.equals(id)) { - t.state = state; - transportsAdapter.notifyDataSetChanged(); - break; - } - } + // TODO } - @UiThread - private static class Transport { - - private TransportId id; - private State state; - private int iconId; - private int textId; - } } diff --git a/briar-android/src/main/res/drawable/ic_baseline_drag_handle.xml b/briar-android/src/main/res/drawable/ic_baseline_drag_handle.xml new file mode 100644 index 000000000..de7bbda0a --- /dev/null +++ b/briar-android/src/main/res/drawable/ic_baseline_drag_handle.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/briar-android/src/main/res/layout/navigation_menu.xml b/briar-android/src/main/res/layout/navigation_menu.xml index 4a7a32c4f..482b362b5 100644 --- a/briar-android/src/main/res/layout/navigation_menu.xml +++ b/briar-android/src/main/res/layout/navigation_menu.xml @@ -1,7 +1,7 @@ - - - - + app:layout_constraintTop_toBottomOf="@+id/navigation" /> diff --git a/briar-android/src/main/res/layout/transports_list.xml b/briar-android/src/main/res/layout/transports_list.xml index a484522c7..43cbd1e87 100644 --- a/briar-android/src/main/res/layout/transports_list.xml +++ b/briar-android/src/main/res/layout/transports_list.xml @@ -1,19 +1,162 @@ - - + - + + + android:layout_marginBottom="8dp" + android:text="@string/transport_internet" + android:textSize="12sp" + app:layout_constraintBottom_toTopOf="@+id/torSwitch" + app:layout_constraintStart_toStartOf="@+id/torIcon" /> - + + + + + + + + + + + + + + + + + diff --git a/briar-android/src/main/res/menu/navigation_drawer.xml b/briar-android/src/main/res/menu/navigation_drawer.xml index 6f3606d0f..c24131755 100644 --- a/briar-android/src/main/res/menu/navigation_drawer.xml +++ b/briar-android/src/main/res/menu/navigation_drawer.xml @@ -1,6 +1,6 @@ -

+ + android:visible="false" + tools:visible="false" /> Sign Out - Internet + Connections + Internet + Tor + Nearby Bluetooth Wi-Fi From 75615a4e7f224b0afa17876303bcbf90b63ba8c9 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Fri, 24 Jan 2020 11:10:54 -0300 Subject: [PATCH 39/69] [android] make transport plugin toggles functional --- .../briarproject/briar/android/AppModule.java | 2 + .../android/activity/ActivityModule.java | 10 -- .../android/navdrawer/NavDrawerActivity.java | 82 ++++----- .../navdrawer/NavDrawerController.java | 23 --- .../android/navdrawer/NavDrawerModule.java | 19 +++ ...ollerImpl.java => NavDrawerViewModel.java} | 158 +++++++++++------- .../navdrawer/PluginViewController.java | 86 ++++++++++ .../navdrawer/TransportStateListener.java | 12 -- .../android/settings/SettingsFragment.java | 80 +-------- .../src/main/res/layout/transports_list.xml | 14 +- briar-android/src/main/res/xml/settings.xml | 27 --- 11 files changed, 257 insertions(+), 256 deletions(-) delete mode 100644 briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerController.java create mode 100644 briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerModule.java rename briar-android/src/main/java/org/briarproject/briar/android/navdrawer/{NavDrawerControllerImpl.java => NavDrawerViewModel.java} (52%) create mode 100644 briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java delete mode 100644 briar-android/src/main/java/org/briarproject/briar/android/navdrawer/TransportStateListener.java 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 7231f9260..85476b1b8 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 @@ -41,6 +41,7 @@ import org.briarproject.bramble.util.StringUtils; import org.briarproject.briar.android.account.LockManagerImpl; import org.briarproject.briar.android.keyagreement.ContactExchangeModule; import org.briarproject.briar.android.login.LoginModule; +import org.briarproject.briar.android.navdrawer.NavDrawerModule; import org.briarproject.briar.android.viewmodel.ViewModelModule; import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.DozeWatchdog; @@ -76,6 +77,7 @@ import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD; @Module(includes = { ContactExchangeModule.class, LoginModule.class, + NavDrawerModule.class, ViewModelModule.class }) public class AppModule { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityModule.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityModule.java index e9ed9686c..5d559ec47 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityModule.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityModule.java @@ -8,8 +8,6 @@ import org.briarproject.briar.android.controller.BriarController; import org.briarproject.briar.android.controller.BriarControllerImpl; import org.briarproject.briar.android.controller.DbController; import org.briarproject.briar.android.controller.DbControllerImpl; -import org.briarproject.briar.android.navdrawer.NavDrawerController; -import org.briarproject.briar.android.navdrawer.NavDrawerControllerImpl; import dagger.Module; import dagger.Provides; @@ -58,14 +56,6 @@ public class ActivityModule { return dbController; } - @ActivityScope - @Provides - NavDrawerController provideNavDrawerController( - NavDrawerControllerImpl navDrawerController) { - activity.addLifecycleController(navDrawerController); - return navDrawerController; - } - @ActivityScope @Provides BriarServiceConnection provideBriarServiceConnection() { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java index e80aec526..44733bb03 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java @@ -19,14 +19,11 @@ import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; -import org.briarproject.bramble.api.plugin.Plugin.State; -import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BriarActivity; import org.briarproject.briar.android.blog.FeedFragment; import org.briarproject.briar.android.contact.ContactListFragment; -import org.briarproject.briar.android.controller.handler.UiResultHandler; import org.briarproject.briar.android.forum.ForumListFragment; import org.briarproject.briar.android.fragment.BaseFragment; import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener; @@ -47,6 +44,8 @@ import androidx.core.app.ActivityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; +import androidx.lifecycle.ViewModelProvider; +import androidx.lifecycle.ViewModelProviders; import static android.view.View.GONE; import static android.view.View.INVISIBLE; @@ -65,8 +64,7 @@ import static org.briarproject.briar.android.util.UiUtils.getDaysUntilExpiry; @MethodsNotNullByDefault @ParametersNotNullByDefault public class NavDrawerActivity extends BriarActivity implements - BaseFragmentListener, TransportStateListener, - OnNavigationItemSelectedListener { + BaseFragmentListener, OnNavigationItemSelectedListener { private static final Logger LOG = getLogger(NavDrawerActivity.class.getName()); @@ -84,10 +82,11 @@ public class NavDrawerActivity extends BriarActivity implements public static Uri SIGN_OUT_URI = Uri.parse("briar-content://org.briarproject.briar/sign-out"); + private NavDrawerViewModel viewModel; private ActionBarDrawerToggle drawerToggle; @Inject - NavDrawerController controller; + ViewModelProvider.Factory viewModelFactory; @Inject LifecycleManager lifecycleManager; @@ -106,6 +105,14 @@ public class NavDrawerActivity extends BriarActivity implements exitIfStartupFailed(getIntent()); setContentView(R.layout.activity_nav_drawer); + viewModel = ViewModelProviders.of(this, viewModelFactory) + .get(NavDrawerViewModel.class); + + viewModel.showExpiryWarning().observe(this, this::showExpiryWarning); + viewModel.shouldAskForDozeWhitelisting().observe(this, ask -> { + if (ask) showDozeDialog(getString(R.string.setup_doze_intro)); + }); + drawerScrollView = findViewById(R.id.drawerScrollView); drawerScrollView.getViewTreeObserver().addOnGlobalLayoutListener( new OnGlobalLayoutListener() { @@ -122,6 +129,7 @@ public class NavDrawerActivity extends BriarActivity implements .removeOnGlobalLayoutListener(this); } }); + new PluginViewController(drawerScrollView, this, viewModel); Toolbar toolbar = findViewById(R.id.toolbar); drawerLayout = findViewById(R.id.drawer_layout); @@ -156,12 +164,7 @@ public class NavDrawerActivity extends BriarActivity implements public void onStart() { super.onStart(); lockManager.checkIfLockable(); - controller.showExpiryWarning(new UiResultHandler(this) { - @Override - public void onResultUi(Boolean expiry) { - if (expiry) showExpiryWarning(); - } - }); + viewModel.checkExpiryWarning(); } @Override @@ -169,16 +172,7 @@ public class NavDrawerActivity extends BriarActivity implements @Nullable Intent data) { super.onActivityResult(request, result, data); if (request == REQUEST_PASSWORD && result == RESULT_OK) { - controller.shouldAskForDozeWhitelisting(this, - new UiResultHandler(this) { - @Override - public void onResultUi(Boolean ask) { - if (ask) { - showDozeDialog( - getString(R.string.setup_doze_intro)); - } - } - }); + viewModel.checkDozeWhitelisting(); } } @@ -348,33 +342,31 @@ public class NavDrawerActivity extends BriarActivity implements if (item != null) item.setVisible(visible); } - private void showExpiryWarning() { + private void showExpiryWarning(boolean show) { int daysUntilExpiry = getDaysUntilExpiry(); - if (daysUntilExpiry < 0) signOut(); + if (daysUntilExpiry < 0) { + signOut(); + return; + } - // show expiry warning text ViewGroup expiryWarning = findViewById(R.id.expiryWarning); - TextView expiryWarningText = - expiryWarning.findViewById(R.id.expiryWarningText); - // make close button functional - ImageView expiryWarningClose = - expiryWarning.findViewById(R.id.expiryWarningClose); - - expiryWarningText.setText(getResources() - .getQuantityString(R.plurals.expiry_warning, - daysUntilExpiry, daysUntilExpiry)); - - expiryWarningClose.setOnClickListener(v -> { - controller.expiryWarningDismissed(); + if (show) { + // show expiry warning text + TextView expiryWarningText = + expiryWarning.findViewById(R.id.expiryWarningText); + String text = getResources().getQuantityString( + R.plurals.expiry_warning, daysUntilExpiry, daysUntilExpiry); + expiryWarningText.setText(text); + // make close button functional + ImageView expiryWarningClose = + expiryWarning.findViewById(R.id.expiryWarningClose); + expiryWarningClose.setOnClickListener(v -> + viewModel.expiryWarningDismissed() + ); + expiryWarning.setVisibility(VISIBLE); + } else { expiryWarning.setVisibility(GONE); - }); - - expiryWarning.setVisibility(VISIBLE); - } - - @Override - public void stateUpdate(TransportId id, State state) { - // TODO + } } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerController.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerController.java deleted file mode 100644 index 5b9c3a963..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerController.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.briarproject.briar.android.navdrawer; - -import android.content.Context; - -import org.briarproject.bramble.api.nullsafety.NotNullByDefault; -import org.briarproject.bramble.api.plugin.Plugin.State; -import org.briarproject.bramble.api.plugin.TransportId; -import org.briarproject.briar.android.controller.ActivityLifecycleController; -import org.briarproject.briar.android.controller.handler.ResultHandler; - -@NotNullByDefault -public interface NavDrawerController extends ActivityLifecycleController { - - State getTransportState(TransportId transportId); - - void showExpiryWarning(ResultHandler handler); - - void expiryWarningDismissed(); - - void shouldAskForDozeWhitelisting(Context ctx, - ResultHandler handler); - -} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerModule.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerModule.java new file mode 100644 index 000000000..966c178a2 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerModule.java @@ -0,0 +1,19 @@ +package org.briarproject.briar.android.navdrawer; + +import org.briarproject.briar.android.viewmodel.ViewModelKey; + +import androidx.lifecycle.ViewModel; +import dagger.Binds; +import dagger.Module; +import dagger.multibindings.IntoMap; + +@Module +public abstract class NavDrawerModule { + + @Binds + @IntoMap + @ViewModelKey(NavDrawerViewModel.class) + abstract ViewModel bindNavDrawerViewModel( + NavDrawerViewModel navDrawerViewModel); + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java similarity index 52% rename from briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java rename to briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java index cbf15708a..89cf1e751 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerControllerImpl.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java @@ -1,31 +1,35 @@ package org.briarproject.briar.android.navdrawer; -import android.app.Activity; -import android.content.Context; +import android.app.Application; import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventListener; -import org.briarproject.bramble.api.lifecycle.LifecycleManager; -import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; -import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.BluetoothConstants; +import org.briarproject.bramble.api.plugin.LanTcpConstants; import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin.State; import org.briarproject.bramble.api.plugin.PluginManager; +import org.briarproject.bramble.api.plugin.TorConstants; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.event.TransportStateEvent; import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.SettingsManager; -import org.briarproject.briar.android.controller.DbControllerImpl; -import org.briarproject.briar.android.controller.handler.ResultHandler; import java.util.concurrent.Executor; import java.util.logging.Logger; import javax.inject.Inject; +import androidx.annotation.Nullable; +import androidx.annotation.UiThread; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; + import static java.util.concurrent.TimeUnit.DAYS; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; @@ -38,52 +42,53 @@ import static org.briarproject.briar.android.controller.BriarControllerImpl.DOZE import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE; import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting; -@MethodsNotNullByDefault -@ParametersNotNullByDefault -public class NavDrawerControllerImpl extends DbControllerImpl - implements NavDrawerController, EventListener { +@NotNullByDefault +public class NavDrawerViewModel extends AndroidViewModel + implements EventListener { private static final Logger LOG = - getLogger(NavDrawerControllerImpl.class.getName()); + getLogger(NavDrawerViewModel.class.getName()); private static final String EXPIRY_DATE_WARNING = "expiryDateWarning"; + static final TransportId[] TRANSPORT_IDS = + {TorConstants.ID, LanTcpConstants.ID, BluetoothConstants.ID}; - private final PluginManager pluginManager; + @DatabaseExecutor + private final Executor dbExecutor; private final SettingsManager settingsManager; + private final PluginManager pluginManager; private final EventBus eventBus; - // UI thread - private TransportStateListener listener; + private final MutableLiveData showExpiryWarning = + new MutableLiveData<>(); + private final MutableLiveData shouldAskForDozeWhitelisting = + new MutableLiveData<>(); + + private final MutableLiveData torPluginState = + new MutableLiveData<>(); + private final MutableLiveData wifiPluginState = + new MutableLiveData<>(); + private final MutableLiveData btPluginState = + new MutableLiveData<>(); @Inject - NavDrawerControllerImpl(@DatabaseExecutor Executor dbExecutor, - LifecycleManager lifecycleManager, PluginManager pluginManager, - SettingsManager settingsManager, EventBus eventBus) { - super(dbExecutor, lifecycleManager); - this.pluginManager = pluginManager; + NavDrawerViewModel(Application app, @DatabaseExecutor Executor dbExecutor, + SettingsManager settingsManager, PluginManager pluginManager, + EventBus eventBus) { + super(app); + this.dbExecutor = dbExecutor; this.settingsManager = settingsManager; + this.pluginManager = pluginManager; this.eventBus = eventBus; - } - - @Override - public void onActivityCreate(Activity activity) { - listener = (TransportStateListener) activity; - } - - @Override - public void onActivityStart() { eventBus.addListener(this); + updatePluginStates(); } @Override - public void onActivityStop() { + protected void onCleared() { eventBus.removeListener(this); } - @Override - public void onActivityDestroy() { - } - @Override public void eventOccurred(Event e) { if (e instanceof TransportStateEvent) { @@ -93,17 +98,22 @@ public class NavDrawerControllerImpl extends DbControllerImpl if (LOG.isLoggable(INFO)) { LOG.info("TransportStateEvent: " + id + " is " + state); } - listener.stateUpdate(id, state); + MutableLiveData liveData = getPluginLiveData(id); + if (liveData != null) liveData.postValue(state); } } - @Override - public void showExpiryWarning(ResultHandler handler) { + LiveData showExpiryWarning() { + return showExpiryWarning; + } + + @UiThread + void checkExpiryWarning() { if (!IS_DEBUG_BUILD) { - handler.onResult(false); + showExpiryWarning.setValue(false); return; } - runOnDbThread(() -> { + dbExecutor.execute(() -> { try { Settings settings = settingsManager.getSettings(SETTINGS_NAMESPACE); @@ -111,7 +121,7 @@ public class NavDrawerControllerImpl extends DbControllerImpl if (warningInt == 0) { // we have not warned before - handler.onResult(true); + showExpiryWarning.postValue(true); } else { long warningLong = warningInt * 1000L; long now = System.currentTimeMillis(); @@ -121,12 +131,12 @@ public class NavDrawerControllerImpl extends DbControllerImpl (EXPIRY_DATE - now) / DAYS.toMillis(1); if (daysSinceLastWarning >= 30) { - handler.onResult(true); + showExpiryWarning.postValue(true); } else if (daysBeforeExpiry <= 3 && daysSinceLastWarning > 0) { - handler.onResult(true); + showExpiryWarning.postValue(true); } else { - handler.onResult(false); + showExpiryWarning.postValue(false); } } } catch (DbException e) { @@ -135,9 +145,10 @@ public class NavDrawerControllerImpl extends DbControllerImpl }); } - @Override - public void expiryWarningDismissed() { - runOnDbThread(() -> { + @UiThread + void expiryWarningDismissed() { + showExpiryWarning.setValue(false); + dbExecutor.execute(() -> { try { Settings settings = new Settings(); int date = (int) (System.currentTimeMillis() / 1000L); @@ -149,31 +160,64 @@ public class NavDrawerControllerImpl extends DbControllerImpl }); } - @Override - public void shouldAskForDozeWhitelisting(Context ctx, - ResultHandler handler) { + LiveData shouldAskForDozeWhitelisting() { + return shouldAskForDozeWhitelisting; + } + + @UiThread + void checkDozeWhitelisting() { // check this first, to hit the DbThread only when really necessary - if (!needsDozeWhitelisting(ctx)) { - handler.onResult(false); + if (!needsDozeWhitelisting(getApplication())) { + shouldAskForDozeWhitelisting.setValue(false); return; } - runOnDbThread(() -> { + dbExecutor.execute(() -> { try { Settings settings = settingsManager.getSettings(SETTINGS_NAMESPACE); boolean ask = settings.getBoolean(DOZE_ASK_AGAIN, true); - handler.onResult(ask); + shouldAskForDozeWhitelisting.postValue(ask); } catch (DbException e) { logException(LOG, WARNING, e); - handler.onResult(true); + shouldAskForDozeWhitelisting.postValue(true); } }); } - @Override - public State getTransportState(TransportId transportId) { - Plugin plugin = pluginManager.getPlugin(transportId); + private void updatePluginStates() { + for (TransportId t : TRANSPORT_IDS) { + MutableLiveData liveData = getPluginLiveData(t); + if (liveData == null) throw new AssertionError(); + liveData.setValue(getTransportState(t)); + } + } + + private State getTransportState(TransportId id) { + Plugin plugin = pluginManager.getPlugin(id); return plugin == null ? DISABLED : plugin.getState(); } + @Nullable + private MutableLiveData getPluginLiveData(TransportId t) { + if (t.equals(TorConstants.ID)) { + return torPluginState; + } else if (t.equals(LanTcpConstants.ID)) { + return wifiPluginState; + } else if (t.equals(BluetoothConstants.ID)) { + return btPluginState; + } else { + return null; + } + } + + LiveData getPluginState(TransportId t) { + LiveData liveData = getPluginLiveData(t); + if (liveData == null) throw new AssertionError(); + return liveData; + } + + void setPluginEnabled(TransportId t, boolean enabled) { + pluginManager.setPluginEnabled(t, enabled); + } + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java new file mode 100644 index 000000000..52995b23e --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java @@ -0,0 +1,86 @@ +package org.briarproject.briar.android.navdrawer; + +import android.view.View; +import android.widget.ImageView; + +import org.briarproject.bramble.api.plugin.BluetoothConstants; +import org.briarproject.bramble.api.plugin.LanTcpConstants; +import org.briarproject.bramble.api.plugin.Plugin.State; +import org.briarproject.bramble.api.plugin.TorConstants; +import org.briarproject.bramble.api.plugin.TransportId; +import org.briarproject.briar.R; + +import androidx.appcompat.widget.SwitchCompat; +import androidx.lifecycle.LifecycleOwner; + +import static androidx.core.content.ContextCompat.getColor; +import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; +import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; +import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING; +import static org.briarproject.briar.android.navdrawer.NavDrawerViewModel.TRANSPORT_IDS; + +class PluginViewController { + + private final ImageView torIcon, wifiIcon, btIcon; + private final SwitchCompat torSwitch, wifiSwitch, btSwitch; + + PluginViewController(View v, LifecycleOwner owner, + NavDrawerViewModel viewModel) { + + torIcon = v.findViewById(R.id.torIcon); + wifiIcon = v.findViewById(R.id.wifiIcon); + btIcon = v.findViewById(R.id.btIcon); + + torSwitch = v.findViewById(R.id.torSwitch); + wifiSwitch = v.findViewById(R.id.wifiSwitch); + btSwitch = v.findViewById(R.id.btSwitch); + + for (TransportId t : TRANSPORT_IDS) { + // a OnCheckedChangeListener would get triggered on programmatic updates + SwitchCompat switchCompat = getSwitch(t); + switchCompat.setOnClickListener(buttonView -> + // TODO check reason first and change settings if needed + viewModel.setPluginEnabled(t, switchCompat.isChecked()) + ); + viewModel.getPluginState(t) + .observe(owner, state -> stateUpdate(t, state)); + } + } + + private void stateUpdate(TransportId id, State state) { + updateIcon(getIcon(id), state); + updateSwitch(getSwitch(id), state); + } + + private SwitchCompat getSwitch(TransportId id) { + if (id == TorConstants.ID) return torSwitch; + if (id == BluetoothConstants.ID) return btSwitch; + if (id == LanTcpConstants.ID) return wifiSwitch; + throw new AssertionError(); + } + + private void updateSwitch(SwitchCompat switchCompat, State state) { + switchCompat.setChecked(state != DISABLED); + } + + private ImageView getIcon(TransportId id) { + if (id == TorConstants.ID) return torIcon; + if (id == BluetoothConstants.ID) return btIcon; + if (id == LanTcpConstants.ID) return wifiIcon; + throw new AssertionError(); + } + + private void updateIcon(ImageView icon, State state) { + int colorRes; + if (state == ACTIVE) { + colorRes = R.color.briar_green_light; + } else if (state == ENABLING) { + colorRes = R.color.briar_yellow; + } else { + colorRes = android.R.color.tertiary_text_light; + } + int color = getColor(icon.getContext(), colorRes); + icon.setColorFilter(color); + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/TransportStateListener.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/TransportStateListener.java deleted file mode 100644 index 54458dcd7..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/TransportStateListener.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.briarproject.briar.android.navdrawer; - -import org.briarproject.bramble.api.plugin.Plugin.State; -import org.briarproject.bramble.api.plugin.TransportId; - -import androidx.annotation.UiThread; - -interface TransportStateListener { - - @UiThread - void stateUpdate(TransportId id, State state); -} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java index 5d284376c..cbf21ad40 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java @@ -20,8 +20,6 @@ import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; -import org.briarproject.bramble.api.plugin.BluetoothConstants; -import org.briarproject.bramble.api.plugin.LanTcpConstants; import org.briarproject.bramble.api.plugin.TorConstants; import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.SettingsManager; @@ -42,6 +40,7 @@ import java.util.logging.Logger; import javax.inject.Inject; +import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.core.content.ContextCompat; import androidx.core.text.TextUtilsCompat; @@ -73,7 +72,6 @@ import static android.widget.Toast.LENGTH_SHORT; import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_LTR; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; -import static org.briarproject.bramble.api.plugin.Plugin.PREF_PLUGIN_ENABLE; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_AUTOMATIC; @@ -112,15 +110,7 @@ public class SettingsFragment extends PreferenceFragmentCompat "pref_key_lock_timeout"; public static final String NOTIFY_SIGN_IN = "pref_key_notify_sign_in"; - private static final String BT_NAMESPACE = - BluetoothConstants.ID.getString(); - private static final String BT_ENABLE = "pref_key_bluetooth"; - - private static final String WIFI_NAMESPACE = LanTcpConstants.ID.getString(); - private static final String WIFI_ENABLE = "pref_key_wifi"; - private static final String TOR_NAMESPACE = TorConstants.ID.getString(); - private static final String TOR_ENABLE = "pref_key_tor_enable"; private static final String TOR_NETWORK = "pref_key_tor_network"; private static final String TOR_MOBILE = "pref_key_tor_mobile_data"; private static final String TOR_ONLY_WHEN_CHARGING = @@ -131,9 +121,6 @@ public class SettingsFragment extends PreferenceFragmentCompat private SettingsActivity listener; private ListPreference language; - private SwitchPreference enableBluetooth; - private SwitchPreference enableWifi; - private SwitchPreference enableTor; private ListPreference torNetwork; private SwitchPreference torMobile; private SwitchPreference torOnlyWhenCharging; @@ -148,7 +135,7 @@ public class SettingsFragment extends PreferenceFragmentCompat private Preference notifySound; // Fields that are accessed from background threads must be volatile - private volatile Settings settings, btSettings, wifiSettings, torSettings; + private volatile Settings settings, torSettings; private volatile boolean settingsLoaded = false; @Inject @@ -177,9 +164,6 @@ public class SettingsFragment extends PreferenceFragmentCompat language = findPreference(LANGUAGE); setLanguageEntries(); ListPreference theme = findPreference("pref_key_theme"); - enableBluetooth = findPreference(BT_ENABLE); - enableWifi = findPreference(WIFI_ENABLE); - enableTor = findPreference(TOR_ENABLE); torNetwork = findPreference(TOR_NETWORK); torMobile = findPreference(TOR_MOBILE); torOnlyWhenCharging = findPreference(TOR_ONLY_WHEN_CHARGING); @@ -211,9 +195,6 @@ public class SettingsFragment extends PreferenceFragmentCompat } return true; }); - enableBluetooth.setOnPreferenceChangeListener(this); - enableWifi.setOnPreferenceChangeListener(this); - enableTor.setOnPreferenceChangeListener(this); torNetwork.setOnPreferenceChangeListener(this); torMobile.setOnPreferenceChangeListener(this); torOnlyWhenCharging.setOnPreferenceChangeListener(this); @@ -256,8 +237,9 @@ public class SettingsFragment extends PreferenceFragmentCompat } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView(LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { View view = super.onCreateView(inflater, container, savedInstanceState); ColorDrawable divider = new ColorDrawable( ContextCompat.getColor(requireContext(), R.color.divider)); @@ -359,8 +341,6 @@ public class SettingsFragment extends PreferenceFragmentCompat try { long start = now(); settings = settingsManager.getSettings(SETTINGS_NAMESPACE); - btSettings = settingsManager.getSettings(BT_NAMESPACE); - wifiSettings = settingsManager.getSettings(WIFI_NAMESPACE); torSettings = settingsManager.getSettings(TOR_NAMESPACE); settingsLoaded = true; logDuration(LOG, "Loading settings", start); @@ -376,18 +356,6 @@ public class SettingsFragment extends PreferenceFragmentCompat // due to events, we might try to display before a load completed if (!settingsLoaded) return; - boolean btEnabledSetting = - btSettings.getBoolean(PREF_PLUGIN_ENABLE, false); - enableBluetooth.setChecked(btEnabledSetting); - - boolean wifiEnabledSetting = - wifiSettings.getBoolean(PREF_PLUGIN_ENABLE, false); - enableWifi.setChecked(wifiEnabledSetting); - - boolean torEnabledSetting = - torSettings.getBoolean(PREF_PLUGIN_ENABLE, true); - enableTor.setChecked(torEnabledSetting); - int torNetworkSetting = torSettings.getInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_AUTOMATIC); torNetwork.setValue(Integer.toString(torNetworkSetting)); @@ -459,9 +427,6 @@ public class SettingsFragment extends PreferenceFragmentCompat // preferences partly needed here, because they have their own logic // - pref_key_lock (screenLock -> displayScreenLockSetting()) // - pref_key_lock_timeout (screenLockTimeout) - enableBluetooth.setEnabled(enabled); - enableWifi.setEnabled(enabled); - enableTor.setEnabled(enabled); torNetwork.setEnabled(enabled); torMobile.setEnabled(enabled); torOnlyWhenCharging.setEnabled(enabled); @@ -563,15 +528,6 @@ public class SettingsFragment extends PreferenceFragmentCompat if (!language.getValue().equals(newValue)) languageChanged((String) newValue); return false; - } else if (preference == enableBluetooth) { - boolean btSetting = (Boolean) newValue; - storeBluetoothSetting(btSetting); - } else if (preference == enableWifi) { - boolean wifiSetting = (Boolean) newValue; - storeWifiSetting(wifiSetting); - } else if (preference == enableTor) { - boolean torEnabledSetting = (Boolean) newValue; - storeTorEnabledSetting(torEnabledSetting); } else if (preference == torNetwork) { int torNetworkSetting = Integer.valueOf((String) newValue); storeTorNetworkSetting(torNetworkSetting); @@ -635,12 +591,6 @@ public class SettingsFragment extends PreferenceFragmentCompat builder.show(); } - private void storeTorEnabledSetting(boolean torEnabledSetting) { - Settings s = new Settings(); - s.putBoolean(PREF_PLUGIN_ENABLE, torEnabledSetting); - mergeSettings(s, TOR_NAMESPACE); - } - private void storeTorNetworkSetting(int torNetworkSetting) { Settings s = new Settings(); s.putInt(PREF_TOR_NETWORK, torNetworkSetting); @@ -659,18 +609,6 @@ public class SettingsFragment extends PreferenceFragmentCompat mergeSettings(s, TOR_NAMESPACE); } - private void storeBluetoothSetting(boolean btSetting) { - Settings s = new Settings(); - s.putBoolean(PREF_PLUGIN_ENABLE, btSetting); - mergeSettings(s, BT_NAMESPACE); - } - - private void storeWifiSetting(boolean wifiSetting) { - Settings s = new Settings(); - s.putBoolean(PREF_PLUGIN_ENABLE, wifiSetting); - mergeSettings(s, WIFI_NAMESPACE); - } - private void storeSettings(Settings s) { mergeSettings(s, SETTINGS_NAMESPACE); } @@ -729,14 +667,6 @@ public class SettingsFragment extends PreferenceFragmentCompat LOG.info("Settings updated"); settings = s.getSettings(); displaySettings(); - } else if (namespace.equals(BT_NAMESPACE)) { - LOG.info("Bluetooth settings updated"); - btSettings = s.getSettings(); - displaySettings(); - } else if (namespace.equals(WIFI_NAMESPACE)) { - LOG.info("Wifi settings updated"); - wifiSettings = s.getSettings(); - displaySettings(); } else if (namespace.equals(TOR_NAMESPACE)) { LOG.info("Tor settings updated"); torSettings = s.getSettings(); diff --git a/briar-android/src/main/res/layout/transports_list.xml b/briar-android/src/main/res/layout/transports_list.xml index 43cbd1e87..0c9f94d84 100644 --- a/briar-android/src/main/res/layout/transports_list.xml +++ b/briar-android/src/main/res/layout/transports_list.xml @@ -14,12 +14,12 @@ android:layout_marginBottom="8dp" android:background="@color/divider" android:src="@drawable/chevron_down_white" - app:tint="?attr/colorControlNormal" app:layout_constraintBottom_toTopOf="@+id/longRangeLabel" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/navigation" app:layout_constraintVertical_bias="1.0" + app:tint="?attr/colorControlNormal" tools:ignore="ContentDescription" tools:visibility="visible" /> @@ -54,8 +54,8 @@ app:layout_constraintBottom_toBottomOf="@+id/torSwitch" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/torSwitch" - app:tint="@color/briar_green" - tools:ignore="ContentDescription" /> + tools:ignore="ContentDescription" + tools:tint="@color/briar_green" /> + tools:ignore="ContentDescription" + tools:tint="@color/briar_green" /> + tools:ignore="ContentDescription" + tools:tint="@color/briar_green" /> - - - - - - Date: Wed, 29 Jan 2020 11:24:11 -0300 Subject: [PATCH 40/69] [android] remove unused strings --- briar-android/src/main/res/values/strings.xml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index ad6347172..44383379a 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -111,7 +111,6 @@ Delete Accept Decline - Options Online Offline Send @@ -193,7 +192,6 @@ Add Contact at a Distance Add contact nearby Add contact at a distance - Give contact a nickname Enter the link from your contact here Contact\'s link Paste @@ -211,7 +209,6 @@ There are pending contact requests Pending Contact Requests No pending contacts - Connecting… Waiting for contact to come online… Connecting… Adding contact… @@ -232,9 +229,6 @@ New contact added. %d new contacts added. - Adding this contact is taking longer than usual - Cannot Connect to Contact - Adding this contact is taking longer than usual.\n\nPlease check that your contact has received your link and added you: No Internet connection Duplicate Link You already have a pending contact with this link: %s @@ -264,11 +258,9 @@ Make Introduction Your introduction has been sent. There was an error making the introduction. - Error when responding to introduction You have asked to introduce %1$s to %2$s. %1$s has asked to introduce you to %2$s. Do you want to add %2$s to your contact list? %1$s has asked to introduce you to %2$s, but %2$s is already in your contact list. Since %1$s might not know that, you can still respond: - %1$s has asked to introduce you to %2$s. You accepted the introduction to %1$s. Before %1$s gets added to your contacts, they need to accept the introduction as well. This might take some time. You declined the introduction to %1$s. @@ -448,9 +440,6 @@ Connections - Connect to contacts via Bluetooth - Connect to contacts on the same Wi-Fi network - Connect to contacts via Internet (Tor) Connection method for Internet (Tor) Automatic based on location Use Tor without bridges @@ -502,8 +491,6 @@ Sign out of Briar if a panic button is pressed Delete Account Delete your Briar account if a panic button is pressed. Caution: This will permanently delete your identities, contacts and messages - Uninstall Briar - This requires manual confirmation in a panic event Notifications From 2d38bd5734dfe69b84f62d64d011e10aac61fa3e Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Wed, 29 Jan 2020 11:25:33 -0300 Subject: [PATCH 41/69] [android] Scroll down when nav drawer chevron is pressed --- .../briar/android/navdrawer/NavDrawerActivity.java | 6 +++++- briar-android/src/main/res/layout/transports_list.xml | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java index 44733bb03..7fa637593 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java @@ -47,6 +47,7 @@ import androidx.fragment.app.FragmentTransaction; import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProviders; +import static android.view.View.FOCUS_DOWN; import static android.view.View.GONE; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; @@ -114,12 +115,12 @@ public class NavDrawerActivity extends BriarActivity implements }); drawerScrollView = findViewById(R.id.drawerScrollView); + View chevronView = drawerScrollView.findViewById(R.id.chevronView); drawerScrollView.getViewTreeObserver().addOnGlobalLayoutListener( new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { // hide/show chevron depending on whether we can scroll - View chevronView = findViewById(R.id.chevronView); if (drawerScrollView.canScrollVertically(1)) { chevronView.setVisibility(VISIBLE); } else { @@ -130,6 +131,9 @@ public class NavDrawerActivity extends BriarActivity implements } }); new PluginViewController(drawerScrollView, this, viewModel); + chevronView.setOnClickListener(v -> + drawerScrollView.fullScroll(FOCUS_DOWN) + ); Toolbar toolbar = findViewById(R.id.toolbar); drawerLayout = findViewById(R.id.drawer_layout); diff --git a/briar-android/src/main/res/layout/transports_list.xml b/briar-android/src/main/res/layout/transports_list.xml index 0c9f94d84..e2558853a 100644 --- a/briar-android/src/main/res/layout/transports_list.xml +++ b/briar-android/src/main/res/layout/transports_list.xml @@ -13,6 +13,7 @@ android:layout_height="24dp" android:layout_marginBottom="8dp" android:background="@color/divider" + android:foreground="?attr/selectableItemBackground" android:src="@drawable/chevron_down_white" app:layout_constraintBottom_toTopOf="@+id/longRangeLabel" app:layout_constraintEnd_toEndOf="parent" From 71b0408fe6c7114de01e7e641d11760d3e5a86c6 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Wed, 29 Jan 2020 14:31:14 +0000 Subject: [PATCH 42/69] Remove "don't connect" option from Tor network setting. This has been replaced by the enable/disable setting and no longer works. --- .../bramble/api/plugin/TorConstants.java | 1 + .../android/settings/SettingsFragment.java | 20 +++++++++++++++++-- briar-android/src/main/res/values/arrays.xml | 2 -- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java index a9ae3c5ae..3a4432d8c 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java @@ -21,6 +21,7 @@ public interface TorConstants { int PREF_TOR_NETWORK_AUTOMATIC = 0; int PREF_TOR_NETWORK_WITHOUT_BRIDGES = 1; int PREF_TOR_NETWORK_WITH_BRIDGES = 2; + // TODO: Remove when settings migration code is removed int PREF_TOR_NETWORK_NEVER = 3; // Reason codes returned by Plugin#getReasonDisabled() diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java index cbf21ad40..9d1d8da7f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java @@ -72,9 +72,11 @@ import static android.widget.Toast.LENGTH_SHORT; import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_LTR; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; +import static org.briarproject.bramble.api.plugin.Plugin.PREF_PLUGIN_ENABLE; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_AUTOMATIC; +import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_NEVER; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ONLY_WHEN_CHARGING; import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logException; @@ -341,7 +343,8 @@ public class SettingsFragment extends PreferenceFragmentCompat try { long start = now(); settings = settingsManager.getSettings(SETTINGS_NAMESPACE); - torSettings = settingsManager.getSettings(TOR_NAMESPACE); + torSettings = migrateTorSettings( + settingsManager.getSettings(TOR_NAMESPACE)); settingsLoaded = true; logDuration(LOG, "Loading settings", start); displaySettings(); @@ -351,6 +354,19 @@ public class SettingsFragment extends PreferenceFragmentCompat }); } + // TODO: Remove after a reasonable migration period (added 2020-01-29) + private Settings migrateTorSettings(Settings s) { + int network = s.getInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_AUTOMATIC); + if (network == PREF_TOR_NETWORK_NEVER) { + s.putInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_AUTOMATIC); + s.putBoolean(PREF_PLUGIN_ENABLE, false); + // We don't need to save the migrated settings - the Tor plugin is + // responsible for that. This code just handles the case where the + // settings are loaded before the plugin migrates them. + } + return s; + } + private void displaySettings() { listener.runOnUiThreadUnlessDestroyed(() -> { // due to events, we might try to display before a load completed @@ -669,7 +685,7 @@ public class SettingsFragment extends PreferenceFragmentCompat displaySettings(); } else if (namespace.equals(TOR_NAMESPACE)) { LOG.info("Tor settings updated"); - torSettings = s.getSettings(); + torSettings = migrateTorSettings(s.getSettings()); displaySettings(); } } diff --git a/briar-android/src/main/res/values/arrays.xml b/briar-android/src/main/res/values/arrays.xml index a9dc6aed6..8a7313b73 100644 --- a/briar-android/src/main/res/values/arrays.xml +++ b/briar-android/src/main/res/values/arrays.xml @@ -4,13 +4,11 @@ @string/tor_network_setting_automatic @string/tor_network_setting_without_bridges @string/tor_network_setting_with_bridges - @string/tor_network_setting_never 0 1 2 - 3 From b04b72402864dfffa1ed8614eb04cdc55cf95b63 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Wed, 29 Jan 2020 14:57:58 +0000 Subject: [PATCH 43/69] Don't show Tor in the enabling state if it's disabled by settings. --- .../briarproject/bramble/plugin/tor/TorPlugin.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index db69469f1..86fcc6a8c 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -191,7 +191,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @Override public void start() throws PluginException { if (used.getAndSet(true)) throw new IllegalStateException(); - state.setStarted(); if (!torDirectory.exists()) { if (!torDirectory.mkdirs()) { LOG.warning("Could not create Tor directory."); @@ -279,7 +278,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } catch (IOException e) { throw new PluginException(e); } - state.setTorStarted(); + state.setStarted(); // Check whether we're online updateConnectionStatus(networkManager.getNetworkStatus(), batteryManager.isCharging()); @@ -863,7 +862,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @GuardedBy("this") private boolean started = false, stopped = false, - torStarted = false, networkInitialised = false, networkEnabled = false, bootstrapped = false, @@ -882,13 +880,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { callback.pluginStateChanged(getState()); } - // Doesn't affect getState() - synchronized void setTorStarted() { - torStarted = true; - } - synchronized boolean isTorRunning() { - return torStarted && !stopped; + return started && !stopped; } @Nullable From a6bd59d3c9d0969a6bf2e662510e71bc5df4ca3b Mon Sep 17 00:00:00 2001 From: akwizgran Date: Wed, 29 Jan 2020 15:21:00 +0000 Subject: [PATCH 44/69] Close small gap between setStarted() and setDisabledBySettings(). --- .../org/briarproject/bramble/plugin/tor/TorPlugin.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index 86fcc6a8c..e2292d063 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -836,7 +836,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } } - state.setDisabledBySettings(disabledBySettings, reasonDisabled); try { @@ -866,6 +865,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { networkEnabled = false, bootstrapped = false, circuitBuilt = false, + settingsChecked = false, disabledBySettings = false; @GuardedBy("this") @@ -914,6 +914,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { synchronized void setDisabledBySettings(boolean disabledBySettings, int reasonDisabled) { + settingsChecked = true; this.disabledBySettings = disabledBySettings; this.reasonDisabled = reasonDisabled; callback.pluginStateChanged(getState()); @@ -932,7 +933,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } synchronized State getState() { - if (!started || stopped || disabledBySettings) return DISABLED; + if (!started || stopped || !settingsChecked || disabledBySettings) { + return DISABLED; + } if (!networkInitialised) return ENABLING; if (!networkEnabled) return INACTIVE; return bootstrapped && circuitBuilt ? ACTIVE : ENABLING; From 802e599f099e3dbad21ccb5abc72c86d06d9f9fe Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 30 Jan 2020 14:49:22 +0000 Subject: [PATCH 45/69] Add STARTING_STOPPING state, use flags for reasons disabled. --- .../api/plugin/BluetoothConstants.java | 3 - .../bramble/api/plugin/Plugin.java | 29 +++-- .../bramble/api/plugin/TorConstants.java | 16 ++- .../bramble/plugin/PluginManagerImpl.java | 4 +- .../plugin/bluetooth/BluetoothPlugin.java | 34 +++--- .../bramble/plugin/tcp/TcpPlugin.java | 13 ++- .../bramble/plugin/tor/TorPlugin.java | 106 +++++++++--------- .../bramble/plugin/modem/ModemPlugin.java | 8 +- .../keyagreement/KeyAgreementActivity.java | 9 +- .../android/navdrawer/NavDrawerViewModel.java | 4 +- .../navdrawer/PluginViewController.java | 16 ++- 11 files changed, 127 insertions(+), 115 deletions(-) diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java index 1752ba10f..c2e506851 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java @@ -8,7 +8,4 @@ public interface BluetoothConstants { String PROP_ADDRESS = "address"; String PROP_UUID = "uuid"; - - // Reason code returned by Plugin#getReasonDisabled() - int REASON_NO_BT_ADAPTER = 2; } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java index 4ecd851e2..9d2d50600 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java @@ -13,8 +13,13 @@ public interface Plugin { enum State { /** - * The plugin has not been started, has been stopped, or is disabled by - * settings. + * The plugin has not finished starting or has been stopped. + */ + STARTING_STOPPING, + + /** + * The plugin is disabled by settings. Use {@link #getReasonsDisabled()} + * to find out which settings are responsible. */ DISABLED, @@ -42,14 +47,7 @@ public interface Plugin { String PREF_PLUGIN_ENABLE = "enable"; /** - * Reason code returned by {@link #getReasonDisabled()} to indicate that - * the plugin is disabled because it has not been started or has been - * stopped. - */ - int REASON_STARTING_STOPPING = 0; - - /** - * Reason code returned by {@link #getReasonDisabled()} to indicate that + * Reason flag returned by {@link #getReasonsDisabled()} to indicate that * the plugin has been disabled by the user. */ int REASON_USER = 1; @@ -85,14 +83,13 @@ public interface Plugin { State getState(); /** - * Returns an integer code indicating why the plugin is - * {@link State#DISABLED disabled}, or -1 if the plugin is not disabled. + * Returns a set of flags indicating why the plugin is + * {@link State#DISABLED disabled}, or 0 if the plugin is not disabled. *

- * The codes used are plugin-specific, except the generic codes - * {@link #REASON_STARTING_STOPPING} and {@link #REASON_USER}, which may - * be used by any plugin. + * The flags used are plugin-specific, except the generic flag + * {@link #REASON_USER}, which may be used by any plugin. */ - int getReasonDisabled(); + int getReasonsDisabled(); /** * Returns true if the plugin should be polled periodically to attempt to diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java index 3a4432d8c..e22d6da4a 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java @@ -24,8 +24,18 @@ public interface TorConstants { // TODO: Remove when settings migration code is removed int PREF_TOR_NETWORK_NEVER = 3; - // Reason codes returned by Plugin#getReasonDisabled() + /** + * Reason flag returned by {@link Plugin#getReasonsDisabled()}. + */ int REASON_BATTERY = 2; - int REASON_MOBILE_DATA = 3; - int REASON_COUNTRY_BLOCKED = 4; + + /** + * Reason flag returned by {@link Plugin#getReasonsDisabled()}. + */ + int REASON_MOBILE_DATA = 4; + + /** + * Reason flag returned by {@link Plugin#getReasonsDisabled()}. + */ + int REASON_COUNTRY_BLOCKED = 8; } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java index fe0f4ebfb..f2d0d6a72 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java @@ -50,7 +50,7 @@ import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.plugin.Plugin.PREF_PLUGIN_ENABLE; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; -import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; +import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.now; @@ -277,7 +277,7 @@ class PluginManagerImpl implements PluginManager, Service { private final TransportId id; private final AtomicReference state = - new AtomicReference<>(DISABLED); + new AtomicReference<>(STARTING_STOPPING); private Callback(TransportId id) { this.id = id; diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java index 4c1133762..85c37a643 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java @@ -49,11 +49,11 @@ import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TR import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID; -import static org.briarproject.bramble.api.plugin.BluetoothConstants.REASON_NO_BT_ADAPTER; import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; +import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; @@ -163,16 +163,15 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { @Override public void start() throws PluginException { if (used.getAndSet(true)) throw new IllegalStateException(); - try { - initialiseAdapter(); - } catch (IOException e) { - state.setNoAdapter(); - throw new PluginException(e); - } - updateProperties(); Settings settings = callback.getSettings(); boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE, false); state.setStarted(enabledByUser); + try { + initialiseAdapter(); + } catch (IOException e) { + throw new PluginException(e); + } + updateProperties(); if (enabledByUser) { if (isAdapterEnabled()) bind(); else enableAdapter(); @@ -256,8 +255,8 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { } @Override - public int getReasonDisabled() { - return state.getReasonDisabled(); + public int getReasonsDisabled() { + return state.getReasonsDisabled(); } @Override @@ -477,7 +476,6 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { @GuardedBy("this") private boolean started = false, stopped = false, - noAdapter = false, enabledByUser = false; @GuardedBy("this") @@ -499,11 +497,6 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { return ss; } - synchronized void setNoAdapter() { - noAdapter = true; - callback.pluginStateChanged(getState()); - } - @Nullable synchronized SS setEnabledByUser(boolean enabledByUser) { this.enabledByUser = enabledByUser; @@ -532,14 +525,13 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { } synchronized State getState() { - if (!started || stopped || !enabledByUser) return DISABLED; + if (!started || stopped) return STARTING_STOPPING; + if (!enabledByUser) return DISABLED; return serverSocket == null ? INACTIVE : ACTIVE; } - synchronized int getReasonDisabled() { - if (noAdapter && !stopped) return REASON_NO_BT_ADAPTER; - if (!started || stopped) return REASON_STARTING_STOPPING; - return enabledByUser ? -1 : REASON_USER; + synchronized int getReasonsDisabled() { + return getState() == DISABLED ? REASON_USER : 0; } } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java index 69b860088..4a8ea8753 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java @@ -51,6 +51,7 @@ import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; +import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; import static org.briarproject.bramble.util.IoUtils.tryToClose; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress; @@ -205,8 +206,8 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { } @Override - public int getReasonDisabled() { - return state.getReasonDisabled(); + public int getReasonsDisabled() { + return state.getReasonsDisabled(); } @Override @@ -455,13 +456,13 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { } synchronized State getState() { - if (!started || stopped || !enabledByUser) return DISABLED; + if (!started || stopped) return STARTING_STOPPING; + if (!enabledByUser) return DISABLED; return serverSocket == null ? INACTIVE : ACTIVE; } - synchronized int getReasonDisabled() { - if (!started || stopped) return REASON_STARTING_STOPPING; - return enabledByUser ? -1 : REASON_USER; + synchronized int getReasonsDisabled() { + return getState() == DISABLED ? REASON_USER : 0; } } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index e2292d063..66a869d0c 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -73,6 +73,7 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; +import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; import static org.briarproject.bramble.api.plugin.TorConstants.CONTROL_PORT; import static org.briarproject.bramble.api.plugin.TorConstants.ID; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE; @@ -533,8 +534,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } @Override - public int getReasonDisabled() { - return state.getReasonDisabled(); + public int getReasonsDisabled() { + return state.getReasonsDisabled(); } @Override @@ -773,7 +774,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { String country = locationUtils.getCurrentCountry(); boolean blocked = circumventionProvider.isTorProbablyBlocked(country); - boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE, true); + boolean enabledByUser = + settings.getBoolean(PREF_PLUGIN_ENABLE, true); int network = settings.getInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_AUTOMATIC); boolean useMobile = settings.getBoolean(PREF_TOR_MOBILE, true); @@ -789,54 +791,58 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { LOG.info("Charging: " + charging); } + int reasonsDisabled = 0; boolean enableNetwork = false, enableBridges = false; boolean useMeek = false, enableConnectionPadding = false; - boolean disabledBySettings = false; - int reasonDisabled = REASON_STARTING_STOPPING; if (!online) { LOG.info("Disabling network, device is offline"); - } else if (!enabledByUser) { - LOG.info("Disabling network, user has disabled Tor"); - disabledBySettings = true; - reasonDisabled = REASON_USER; - } else if (!charging && onlyWhenCharging) { - LOG.info("Disabling network, device is on battery"); - disabledBySettings = true; - reasonDisabled = REASON_BATTERY; - } else if (!useMobile && !wifi) { - LOG.info("Disabling network, device is using mobile data"); - disabledBySettings = true; - reasonDisabled = REASON_MOBILE_DATA; - } else if (automatic && blocked && !bridgesWork) { - LOG.info("Disabling network, country is blocked"); - disabledBySettings = true; - reasonDisabled = REASON_COUNTRY_BLOCKED; } else { - LOG.info("Enabling network"); - enableNetwork = true; - if (network == PREF_TOR_NETWORK_WITH_BRIDGES || - (automatic && bridgesWork)) { - if (circumventionProvider.needsMeek(country)) { - LOG.info("Using meek bridges"); - enableBridges = true; - useMeek = true; - } else { - LOG.info("Using obfs4 bridges"); - enableBridges = true; - } - } else { - LOG.info("Not using bridges"); + if (!enabledByUser) { + LOG.info("User has disabled Tor"); + reasonsDisabled |= REASON_USER; } - if (wifi && charging) { - LOG.info("Enabling connection padding"); - enableConnectionPadding = true; + if (!charging && onlyWhenCharging) { + LOG.info("Configured not to use battery"); + reasonsDisabled |= REASON_BATTERY; + } + if (!useMobile && !wifi) { + LOG.info("Configured not to use mobile data"); + reasonsDisabled |= REASON_MOBILE_DATA; + } + if (automatic && blocked && !bridgesWork) { + LOG.info("Country is blocked"); + reasonsDisabled |= REASON_COUNTRY_BLOCKED; + } + + if (reasonsDisabled != 0) { + LOG.info("Disabling network due to settings"); } else { - LOG.info("Disabling connection padding"); + LOG.info("Enabling network"); + enableNetwork = true; + if (network == PREF_TOR_NETWORK_WITH_BRIDGES || + (automatic && bridgesWork)) { + if (circumventionProvider.needsMeek(country)) { + LOG.info("Using meek bridges"); + enableBridges = true; + useMeek = true; + } else { + LOG.info("Using obfs4 bridges"); + enableBridges = true; + } + } else { + LOG.info("Not using bridges"); + } + if (wifi && charging) { + LOG.info("Enabling connection padding"); + enableConnectionPadding = true; + } else { + LOG.info("Disabling connection padding"); + } } } - state.setDisabledBySettings(disabledBySettings, reasonDisabled); + state.setReasonsDisabled(reasonsDisabled); try { if (enableNetwork) { @@ -865,11 +871,10 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { networkEnabled = false, bootstrapped = false, circuitBuilt = false, - settingsChecked = false, - disabledBySettings = false; + settingsChecked = false; @GuardedBy("this") - private int reasonDisabled = REASON_STARTING_STOPPING; + private int reasonsDisabled = 0; @GuardedBy("this") @Nullable @@ -912,11 +917,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { callback.pluginStateChanged(getState()); } - synchronized void setDisabledBySettings(boolean disabledBySettings, - int reasonDisabled) { + synchronized void setReasonsDisabled(int reasonsDisabled) { settingsChecked = true; - this.disabledBySettings = disabledBySettings; - this.reasonDisabled = reasonDisabled; + this.reasonsDisabled = reasonsDisabled; callback.pluginStateChanged(getState()); } @@ -933,16 +936,17 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } synchronized State getState() { - if (!started || stopped || !settingsChecked || disabledBySettings) { - return DISABLED; + if (!started || stopped || !settingsChecked) { + return STARTING_STOPPING; } + if (reasonsDisabled != 0) return DISABLED; if (!networkInitialised) return ENABLING; if (!networkEnabled) return INACTIVE; return bootstrapped && circuitBuilt ? ACTIVE : ENABLING; } - synchronized int getReasonDisabled() { - return getState() == DISABLED ? reasonDisabled : -1; + synchronized int getReasonsDisabled() { + return getState() == DISABLED ? reasonsDisabled : 0; } } } diff --git a/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java b/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java index e2eb69ca8..53a668f20 100644 --- a/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java +++ b/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java @@ -31,9 +31,9 @@ import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; -import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; +import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; @@ -121,8 +121,8 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { } @Override - public int getReasonDisabled() { - return getState() == DISABLED ? REASON_STARTING_STOPPING : -1; + public int getReasonsDisabled() { + return 0; } @Override @@ -280,7 +280,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { } private State getState() { - if (!started || stopped) return DISABLED; + if (!started || stopped) return STARTING_STOPPING; if (failed) return INACTIVE; return initialised ? ACTIVE : ENABLING; } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java index 72e7b69c8..f55d89a49 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java @@ -53,6 +53,7 @@ import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; +import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_BLUETOOTH_DISCOVERABLE; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA_LOCATION; @@ -258,7 +259,9 @@ public abstract class KeyAgreementActivity extends BriarActivity implements private boolean shouldEnableWifi() { if (hasEnabledWifi) return false; Plugin p = pluginManager.getPlugin(LanTcpConstants.ID); - return p != null && p.getState() == DISABLED; + if (p == null) return false; + State state = p.getState(); + return state == STARTING_STOPPING || state == DISABLED; } private void requestBluetoothDiscoverable() { @@ -284,7 +287,9 @@ public abstract class KeyAgreementActivity extends BriarActivity implements if (bluetoothDecision != BluetoothDecision.ACCEPTED) return false; if (hasEnabledBluetooth) return false; Plugin p = pluginManager.getPlugin(BluetoothConstants.ID); - return p != null && p.getState() == DISABLED; + if (p == null) return false; + State state = p.getState(); + return state == STARTING_STOPPING || state == DISABLED; } @Override diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java index 89cf1e751..da95ef228 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java @@ -34,7 +34,7 @@ import static java.util.concurrent.TimeUnit.DAYS; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; -import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; +import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE; import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD; @@ -194,7 +194,7 @@ public class NavDrawerViewModel extends AndroidViewModel private State getTransportState(TransportId id) { Plugin plugin = pluginManager.getPlugin(id); - return plugin == null ? DISABLED : plugin.getState(); + return plugin == null ? STARTING_STOPPING : plugin.getState(); } @Nullable diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java index 52995b23e..075125959 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java @@ -17,6 +17,7 @@ import static androidx.core.content.ContextCompat.getColor; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING; +import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; import static org.briarproject.briar.android.navdrawer.NavDrawerViewModel.TRANSPORT_IDS; class PluginViewController { @@ -38,10 +39,13 @@ class PluginViewController { for (TransportId t : TRANSPORT_IDS) { // a OnCheckedChangeListener would get triggered on programmatic updates SwitchCompat switchCompat = getSwitch(t); - switchCompat.setOnClickListener(buttonView -> - // TODO check reason first and change settings if needed - viewModel.setPluginEnabled(t, switchCompat.isChecked()) - ); + switchCompat.setOnClickListener(buttonView -> { + // TODO check reason first and change settings if needed + viewModel.setPluginEnabled(t, switchCompat.isChecked()); + // Revert the switch to its previous state until the plugin + // changes its state + switchCompat.toggle(); + }); viewModel.getPluginState(t) .observe(owner, state -> stateUpdate(t, state)); } @@ -60,7 +64,9 @@ class PluginViewController { } private void updateSwitch(SwitchCompat switchCompat, State state) { - switchCompat.setChecked(state != DISABLED); + boolean checked = state != STARTING_STOPPING && state != DISABLED; + switchCompat.setChecked(checked); + switchCompat.setEnabled(state != STARTING_STOPPING); } private ImageView getIcon(TransportId id) { From 3ab88181ebbbc56b44590f16d65368f374192b27 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Wed, 5 Feb 2020 17:15:19 +0000 Subject: [PATCH 46/69] Put the transport toggles in an expandable view (no animations). --- .../artwork/transports_list_scene.xml | 117 ------ .../android/navdrawer/NavDrawerActivity.java | 25 +- .../navdrawer/PluginViewController.java | 53 ++- .../src/main/res/layout/navigation_menu.xml | 15 +- .../src/main/res/layout/transports_list.xml | 366 +++++++++++------- 5 files changed, 268 insertions(+), 308 deletions(-) delete mode 100644 briar-android/artwork/transports_list_scene.xml diff --git a/briar-android/artwork/transports_list_scene.xml b/briar-android/artwork/transports_list_scene.xml deleted file mode 100644 index bcb0d82ea..000000000 --- a/briar-android/artwork/transports_list_scene.xml +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java index 7fa637593..d1a5ea458 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java @@ -7,9 +7,7 @@ import android.os.Bundle; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.ImageView; -import android.widget.ScrollView; import android.widget.TextView; import com.google.android.material.navigation.NavigationView; @@ -47,9 +45,7 @@ import androidx.fragment.app.FragmentTransaction; import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProviders; -import static android.view.View.FOCUS_DOWN; import static android.view.View.GONE; -import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; import static androidx.core.view.GravityCompat.START; import static androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED; @@ -92,7 +88,6 @@ public class NavDrawerActivity extends BriarActivity implements LifecycleManager lifecycleManager; private DrawerLayout drawerLayout; - private ScrollView drawerScrollView; private NavigationView navigation; @Override @@ -114,26 +109,8 @@ public class NavDrawerActivity extends BriarActivity implements if (ask) showDozeDialog(getString(R.string.setup_doze_intro)); }); - drawerScrollView = findViewById(R.id.drawerScrollView); - View chevronView = drawerScrollView.findViewById(R.id.chevronView); - drawerScrollView.getViewTreeObserver().addOnGlobalLayoutListener( - new OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - // hide/show chevron depending on whether we can scroll - if (drawerScrollView.canScrollVertically(1)) { - chevronView.setVisibility(VISIBLE); - } else { - chevronView.setVisibility(INVISIBLE); - } - drawerScrollView.getViewTreeObserver() - .removeOnGlobalLayoutListener(this); - } - }); + View drawerScrollView = findViewById(R.id.drawerScrollView); new PluginViewController(drawerScrollView, this, viewModel); - chevronView.setOnClickListener(v -> - drawerScrollView.fullScroll(FOCUS_DOWN) - ); Toolbar toolbar = findViewById(R.id.toolbar); drawerLayout = findViewById(R.id.drawer_layout); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java index 075125959..7206d98e3 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java @@ -2,6 +2,7 @@ package org.briarproject.briar.android.navdrawer; import android.view.View; import android.widget.ImageView; +import android.widget.ScrollView; import org.briarproject.bramble.api.plugin.BluetoothConstants; import org.briarproject.bramble.api.plugin.LanTcpConstants; @@ -13,6 +14,9 @@ import org.briarproject.briar.R; import androidx.appcompat.widget.SwitchCompat; import androidx.lifecycle.LifecycleOwner; +import static android.view.View.FOCUS_DOWN; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; import static androidx.core.content.ContextCompat.getColor; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; @@ -22,15 +26,38 @@ import static org.briarproject.briar.android.navdrawer.NavDrawerViewModel.TRANSP class PluginViewController { - private final ImageView torIcon, wifiIcon, btIcon; + private final ImageView torIconExpanded, torIconCollapsed; + private final ImageView wifiIconExpanded, wifiIconCollapsed; + private final ImageView btIconExpanded, btIconCollapsed; private final SwitchCompat torSwitch, wifiSwitch, btSwitch; PluginViewController(View v, LifecycleOwner owner, NavDrawerViewModel viewModel) { - torIcon = v.findViewById(R.id.torIcon); - wifiIcon = v.findViewById(R.id.wifiIcon); - btIcon = v.findViewById(R.id.btIcon); + ScrollView scrollView = v.findViewById(R.id.drawerScrollView); + View expandedLayout = v.findViewById(R.id.expandedLayout); + View collapsedLayout = v.findViewById(R.id.collapsedLayout); + + expandedLayout.addOnLayoutChangeListener((view, left, top, right, + bottom, oldLeft, oldTop, oldRight, oldBottom) -> + scrollView.fullScroll(FOCUS_DOWN)); + + v.findViewById(R.id.chevronViewCollapsed).setOnClickListener(view -> { + expandedLayout.setVisibility(VISIBLE); + collapsedLayout.setVisibility(GONE); + }); + + v.findViewById(R.id.chevronViewExpanded).setOnClickListener(view -> { + expandedLayout.setVisibility(GONE); + collapsedLayout.setVisibility(VISIBLE); + }); + + torIconExpanded = v.findViewById(R.id.torIconExpanded); + torIconCollapsed = v.findViewById(R.id.torIconCollapsed); + wifiIconExpanded = v.findViewById(R.id.wifiIconExpanded); + wifiIconCollapsed = v.findViewById(R.id.wifiIconCollapsed); + btIconExpanded = v.findViewById(R.id.btIconExpanded); + btIconCollapsed = v.findViewById(R.id.btIconCollapsed); torSwitch = v.findViewById(R.id.torSwitch); wifiSwitch = v.findViewById(R.id.wifiSwitch); @@ -52,7 +79,8 @@ class PluginViewController { } private void stateUpdate(TransportId id, State state) { - updateIcon(getIcon(id), state); + updateIcon(getExpandedIcon(id), state); + updateIcon(getCollapsedIcon(id), state); updateSwitch(getSwitch(id), state); } @@ -69,10 +97,17 @@ class PluginViewController { switchCompat.setEnabled(state != STARTING_STOPPING); } - private ImageView getIcon(TransportId id) { - if (id == TorConstants.ID) return torIcon; - if (id == BluetoothConstants.ID) return btIcon; - if (id == LanTcpConstants.ID) return wifiIcon; + private ImageView getExpandedIcon(TransportId id) { + if (id == TorConstants.ID) return torIconExpanded; + if (id == BluetoothConstants.ID) return btIconExpanded; + if (id == LanTcpConstants.ID) return wifiIconExpanded; + throw new AssertionError(); + } + + private ImageView getCollapsedIcon(TransportId id) { + if (id == TorConstants.ID) return torIconCollapsed; + if (id == BluetoothConstants.ID) return btIconCollapsed; + if (id == LanTcpConstants.ID) return wifiIconCollapsed; throw new AssertionError(); } diff --git a/briar-android/src/main/res/layout/navigation_menu.xml b/briar-android/src/main/res/layout/navigation_menu.xml index 482b362b5..394a3d7f2 100644 --- a/briar-android/src/main/res/layout/navigation_menu.xml +++ b/briar-android/src/main/res/layout/navigation_menu.xml @@ -10,7 +10,9 @@ + android:layout_height="wrap_content" + android:background="@color/window_background" + android:orientation="vertical"> - + diff --git a/briar-android/src/main/res/layout/transports_list.xml b/briar-android/src/main/res/layout/transports_list.xml index e2558853a..019e98615 100644 --- a/briar-android/src/main/res/layout/transports_list.xml +++ b/briar-android/src/main/res/layout/transports_list.xml @@ -2,162 +2,234 @@ - - - - - - - - - - - - - - - - - - - + android:visibility="gone"> - + + + + + + + + + + + + + + + + + + + + + + + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintVertical_bias="1.0"> + + + + + + + + + + + + + From d16a301fc47ecd0c0472861fc614404690039de4 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 6 Feb 2020 13:58:25 +0000 Subject: [PATCH 47/69] Make entire collapsed view clickable. --- .../navdrawer/PluginViewController.java | 2 +- .../src/main/res/layout/transports_list.xml | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java index 7206d98e3..033908a76 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java @@ -42,7 +42,7 @@ class PluginViewController { bottom, oldLeft, oldTop, oldRight, oldBottom) -> scrollView.fullScroll(FOCUS_DOWN)); - v.findViewById(R.id.chevronViewCollapsed).setOnClickListener(view -> { + collapsedLayout.setOnClickListener(view -> { expandedLayout.setVisibility(VISIBLE); collapsedLayout.setVisibility(GONE); }); diff --git a/briar-android/src/main/res/layout/transports_list.xml b/briar-android/src/main/res/layout/transports_list.xml index 019e98615..de9be7838 100644 --- a/briar-android/src/main/res/layout/transports_list.xml +++ b/briar-android/src/main/res/layout/transports_list.xml @@ -8,12 +8,12 @@ android:id="@+id/expandedLayout" android:layout_width="0dp" android:layout_height="wrap_content" - app:layout_constraintTop_toBottomOf="@+id/navigation" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent" + android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintVertical_bias="1.0" - android:visibility="gone"> + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/navigation" + app:layout_constraintVertical_bias="1.0"> Date: Mon, 10 Feb 2020 14:08:36 +0000 Subject: [PATCH 48/69] Transition from one constraint set to another. --- .../navdrawer/PluginViewController.java | 84 ++++--- .../src/main/res/layout/navigation_menu.xml | 28 +-- .../res/layout/navigation_menu_collapsed.xml | 165 ++++++++++++ .../res/layout/navigation_menu_expanded.xml | 183 ++++++++++++++ .../src/main/res/layout/transports_list.xml | 237 ------------------ 5 files changed, 398 insertions(+), 299 deletions(-) create mode 100644 briar-android/src/main/res/layout/navigation_menu_collapsed.xml create mode 100644 briar-android/src/main/res/layout/navigation_menu_expanded.xml delete mode 100644 briar-android/src/main/res/layout/transports_list.xml diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java index 033908a76..f8776e566 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java @@ -11,12 +11,15 @@ import org.briarproject.bramble.api.plugin.TorConstants; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.briar.R; +import androidx.appcompat.widget.AppCompatImageButton; import androidx.appcompat.widget.SwitchCompat; +import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.constraintlayout.widget.ConstraintSet; import androidx.lifecycle.LifecycleOwner; +import static android.os.Build.VERSION.SDK_INT; +import static android.transition.TransitionManager.beginDelayedTransition; import static android.view.View.FOCUS_DOWN; -import static android.view.View.GONE; -import static android.view.View.VISIBLE; import static androidx.core.content.ContextCompat.getColor; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; @@ -26,38 +29,43 @@ import static org.briarproject.briar.android.navdrawer.NavDrawerViewModel.TRANSP class PluginViewController { - private final ImageView torIconExpanded, torIconCollapsed; - private final ImageView wifiIconExpanded, wifiIconCollapsed; - private final ImageView btIconExpanded, btIconCollapsed; + private final ConstraintLayout drawerContent; + private final ConstraintSet collapsedConstraints, expandedConstraints; + private final AppCompatImageButton chevronView; + private final ImageView torIcon, wifiIcon, btIcon; private final SwitchCompat torSwitch, wifiSwitch, btSwitch; + private boolean expanded = false; + PluginViewController(View v, LifecycleOwner owner, NavDrawerViewModel viewModel) { + drawerContent = v.findViewById(R.id.drawerContent); + collapsedConstraints = new ConstraintSet(); + collapsedConstraints.clone(v.getContext(), + R.layout.navigation_menu_collapsed); + + expandedConstraints = new ConstraintSet(); + expandedConstraints.clone(v.getContext(), + R.layout.navigation_menu_expanded); + + // Scroll the drawer to the bottom when the view is expanded/collapsed ScrollView scrollView = v.findViewById(R.id.drawerScrollView); - View expandedLayout = v.findViewById(R.id.expandedLayout); - View collapsedLayout = v.findViewById(R.id.collapsedLayout); - - expandedLayout.addOnLayoutChangeListener((view, left, top, right, + drawerContent.addOnLayoutChangeListener((view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> scrollView.fullScroll(FOCUS_DOWN)); - collapsedLayout.setOnClickListener(view -> { - expandedLayout.setVisibility(VISIBLE); - collapsedLayout.setVisibility(GONE); - }); + // Clicking the chevron expands or collapses the view + chevronView = v.findViewById(R.id.chevronView); + chevronView.setOnClickListener(view -> expandOrCollapseView()); - v.findViewById(R.id.chevronViewExpanded).setOnClickListener(view -> { - expandedLayout.setVisibility(GONE); - collapsedLayout.setVisibility(VISIBLE); - }); + // The whole view is clickable when collapsed + v.findViewById(R.id.connectionsBackground).setOnClickListener(view -> + expandOrCollapseView()); - torIconExpanded = v.findViewById(R.id.torIconExpanded); - torIconCollapsed = v.findViewById(R.id.torIconCollapsed); - wifiIconExpanded = v.findViewById(R.id.wifiIconExpanded); - wifiIconCollapsed = v.findViewById(R.id.wifiIconCollapsed); - btIconExpanded = v.findViewById(R.id.btIconExpanded); - btIconCollapsed = v.findViewById(R.id.btIconCollapsed); + torIcon = v.findViewById(R.id.torIcon); + wifiIcon = v.findViewById(R.id.wifiIcon); + btIcon = v.findViewById(R.id.btIcon); torSwitch = v.findViewById(R.id.torSwitch); wifiSwitch = v.findViewById(R.id.wifiSwitch); @@ -78,9 +86,20 @@ class PluginViewController { } } + private void expandOrCollapseView() { + if (SDK_INT >= 19) beginDelayedTransition(drawerContent); + if (expanded) { + collapsedConstraints.applyTo(drawerContent); + chevronView.setImageResource(R.drawable.chevron_up_white); + } else { + expandedConstraints.applyTo(drawerContent); + chevronView.setImageResource(R.drawable.chevron_down_white); + } + expanded = !expanded; + } + private void stateUpdate(TransportId id, State state) { - updateIcon(getExpandedIcon(id), state); - updateIcon(getCollapsedIcon(id), state); + updateIcon(getIcon(id), state); updateSwitch(getSwitch(id), state); } @@ -97,17 +116,10 @@ class PluginViewController { switchCompat.setEnabled(state != STARTING_STOPPING); } - private ImageView getExpandedIcon(TransportId id) { - if (id == TorConstants.ID) return torIconExpanded; - if (id == BluetoothConstants.ID) return btIconExpanded; - if (id == LanTcpConstants.ID) return wifiIconExpanded; - throw new AssertionError(); - } - - private ImageView getCollapsedIcon(TransportId id) { - if (id == TorConstants.ID) return torIconCollapsed; - if (id == BluetoothConstants.ID) return btIconCollapsed; - if (id == LanTcpConstants.ID) return wifiIconCollapsed; + private ImageView getIcon(TransportId id) { + if (id == TorConstants.ID) return torIcon; + if (id == BluetoothConstants.ID) return btIcon; + if (id == LanTcpConstants.ID) return wifiIcon; throw new AssertionError(); } diff --git a/briar-android/src/main/res/layout/navigation_menu.xml b/briar-android/src/main/res/layout/navigation_menu.xml index 394a3d7f2..f23d4bda9 100644 --- a/briar-android/src/main/res/layout/navigation_menu.xml +++ b/briar-android/src/main/res/layout/navigation_menu.xml @@ -1,6 +1,5 @@ - + - - - - - - - + \ No newline at end of file diff --git a/briar-android/src/main/res/layout/navigation_menu_collapsed.xml b/briar-android/src/main/res/layout/navigation_menu_collapsed.xml new file mode 100644 index 000000000..11daa10fc --- /dev/null +++ b/briar-android/src/main/res/layout/navigation_menu_collapsed.xml @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/briar-android/src/main/res/layout/navigation_menu_expanded.xml b/briar-android/src/main/res/layout/navigation_menu_expanded.xml new file mode 100644 index 000000000..1573a9917 --- /dev/null +++ b/briar-android/src/main/res/layout/navigation_menu_expanded.xml @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/briar-android/src/main/res/layout/transports_list.xml b/briar-android/src/main/res/layout/transports_list.xml deleted file mode 100644 index de9be7838..000000000 --- a/briar-android/src/main/res/layout/transports_list.xml +++ /dev/null @@ -1,237 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From d689cf776c769fd8f8c3e006f2a09a7112f80b9b Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 6 Feb 2020 13:38:48 +0000 Subject: [PATCH 49/69] Change Tor settings after asking for confirmation. --- .../plugin/tor/CircumventionProvider.java | 2 +- .../android/navdrawer/NavDrawerActivity.java | 49 ++++++++++++++++++- .../android/navdrawer/NavDrawerViewModel.java | 28 +++++++++++ .../navdrawer/PluginViewController.java | 28 ++++++++++- .../android/settings/SettingsFragment.java | 10 ++-- .../briar/android/util/UiUtils.java | 12 +++++ briar-android/src/main/res/values/strings.xml | 5 ++ 7 files changed, 123 insertions(+), 11 deletions(-) diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProvider.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProvider.java index 1e670de96..944399799 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProvider.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProvider.java @@ -17,7 +17,7 @@ public interface CircumventionProvider { String[] BLOCKED = {"CN", "IR", "EG", "BY", "TR", "SY", "VE"}; /** - * Countries where obfs4 bridge connection are likely to work. + * Countries where obfs4 or meek bridge connections are likely to work. * Should be a subset of {@link #BLOCKED}. */ String[] BRIDGES = { "CN", "IR", "EG", "BY", "TR", "SY", "VE" }; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java index d1a5ea458..91b2e147c 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java @@ -17,6 +17,7 @@ import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; +import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BriarActivity; @@ -37,6 +38,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBarDrawerToggle; +import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.Toolbar; import androidx.core.app.ActivityCompat; import androidx.drawerlayout.widget.DrawerLayout; @@ -53,9 +55,13 @@ import static androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE; import static java.util.Objects.requireNonNull; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING; +import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY; +import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED; +import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA; import static org.briarproject.briar.android.BriarService.EXTRA_STARTUP_FAILED; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD; import static org.briarproject.briar.android.navdrawer.IntentRouter.handleExternalIntent; +import static org.briarproject.briar.android.util.UiUtils.getCountryDisplayName; import static org.briarproject.briar.android.util.UiUtils.getDaysUntilExpiry; @MethodsNotNullByDefault @@ -84,9 +90,13 @@ public class NavDrawerActivity extends BriarActivity implements @Inject ViewModelProvider.Factory viewModelFactory; + @Inject LifecycleManager lifecycleManager; + @Inject + LocationUtils locationUtils; + private DrawerLayout drawerLayout; private NavigationView navigation; @@ -110,7 +120,10 @@ public class NavDrawerActivity extends BriarActivity implements }); View drawerScrollView = findViewById(R.id.drawerScrollView); - new PluginViewController(drawerScrollView, this, viewModel); + PluginViewController pluginViewController = + new PluginViewController(drawerScrollView, this, viewModel); + pluginViewController.getReasonsTorDisabled().observeEvent(this, + this::showTorSettingsDialog); Toolbar toolbar = findViewById(R.id.toolbar); drawerLayout = findViewById(R.id.drawer_layout); @@ -350,4 +363,38 @@ public class NavDrawerActivity extends BriarActivity implements } } + private void showTorSettingsDialog(int reasonsDisabled) { + boolean battery = (reasonsDisabled & REASON_BATTERY) != 0; + boolean mobileData = (reasonsDisabled & REASON_MOBILE_DATA) != 0; + boolean location = (reasonsDisabled & REASON_COUNTRY_BLOCKED) != 0; + + StringBuilder s = new StringBuilder(); + if (location) { + s.append("\t\u2022 "); + s.append(getString(R.string.tor_override_network_setting, + getCountryDisplayName(locationUtils.getCurrentCountry()))); + s.append('\n'); + } + if (mobileData) { + s.append("\t\u2022 "); + s.append(getString(R.string.tor_override_mobile_data_setting)); + s.append('\n'); + } + if (battery) { + s.append("\t\u2022 "); + s.append(getString(R.string.tor_only_when_charging_title)); + s.append('\n'); + } + String message = + getString(R.string.tor_override_settings_body, s.toString()); + + AlertDialog.Builder b = + new AlertDialog.Builder(this, R.style.BriarDialogTheme); + b.setMessage(message); + b.setPositiveButton(R.string.continue_button, (dialog, which) -> + viewModel.setTorEnabled(battery, mobileData, location)); + b.setNegativeButton(R.string.cancel, (dialog, which) -> + dialog.dismiss()); + b.show(); + } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java index da95ef228..8f1909541 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java @@ -34,7 +34,12 @@ import static java.util.concurrent.TimeUnit.DAYS; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; +import static org.briarproject.bramble.api.plugin.Plugin.PREF_PLUGIN_ENABLE; import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; +import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE; +import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK; +import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_WITH_BRIDGES; +import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ONLY_WHEN_CHARGING; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE; import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD; @@ -216,8 +221,31 @@ public class NavDrawerViewModel extends AndroidViewModel return liveData; } + int getReasonsDisabled(TransportId id) { + Plugin plugin = pluginManager.getPlugin(id); + return plugin == null ? 0 : plugin.getReasonsDisabled(); + } + void setPluginEnabled(TransportId t, boolean enabled) { pluginManager.setPluginEnabled(t, enabled); } + void setTorEnabled(boolean battery, boolean mobileData, boolean location) { + Plugin plugin = pluginManager.getPlugin(TorConstants.ID); + if (plugin == null) return; + + Settings s = new Settings(); + s.putBoolean(PREF_PLUGIN_ENABLE, true); + if (battery) s.putBoolean(PREF_TOR_ONLY_WHEN_CHARGING, false); + if (mobileData) s.putBoolean(PREF_TOR_MOBILE, true); + if (location) s.putInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_WITH_BRIDGES); + dbExecutor.execute(() -> { + try { + settingsManager.mergeSettings(s, TorConstants.ID.getString()); + } catch (DbException e) { + logException(LOG, WARNING, e); + } + }); + } + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java index f8776e566..ea3de6d2d 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java @@ -10,6 +10,8 @@ import org.briarproject.bramble.api.plugin.Plugin.State; import org.briarproject.bramble.api.plugin.TorConstants; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.briar.R; +import org.briarproject.briar.android.viewmodel.LiveEvent; +import org.briarproject.briar.android.viewmodel.MutableLiveEvent; import androidx.appcompat.widget.AppCompatImageButton; import androidx.appcompat.widget.SwitchCompat; @@ -21,6 +23,7 @@ import static android.os.Build.VERSION.SDK_INT; import static android.transition.TransitionManager.beginDelayedTransition; import static android.view.View.FOCUS_DOWN; import static androidx.core.content.ContextCompat.getColor; +import static org.briarproject.bramble.api.plugin.Plugin.REASON_USER; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING; @@ -29,16 +32,20 @@ import static org.briarproject.briar.android.navdrawer.NavDrawerViewModel.TRANSP class PluginViewController { + private final NavDrawerViewModel viewModel; private final ConstraintLayout drawerContent; private final ConstraintSet collapsedConstraints, expandedConstraints; private final AppCompatImageButton chevronView; private final ImageView torIcon, wifiIcon, btIcon; private final SwitchCompat torSwitch, wifiSwitch, btSwitch; + private final MutableLiveEvent reasonsTorDisabled = + new MutableLiveEvent<>(); private boolean expanded = false; PluginViewController(View v, LifecycleOwner owner, NavDrawerViewModel viewModel) { + this.viewModel = viewModel; drawerContent = v.findViewById(R.id.drawerContent); collapsedConstraints = new ConstraintSet(); @@ -75,8 +82,8 @@ class PluginViewController { // a OnCheckedChangeListener would get triggered on programmatic updates SwitchCompat switchCompat = getSwitch(t); switchCompat.setOnClickListener(buttonView -> { - // TODO check reason first and change settings if needed - viewModel.setPluginEnabled(t, switchCompat.isChecked()); + if (switchCompat.isChecked()) tryToEnablePlugin(t); + else viewModel.setPluginEnabled(t, false); // Revert the switch to its previous state until the plugin // changes its state switchCompat.toggle(); @@ -98,6 +105,23 @@ class PluginViewController { expanded = !expanded; } + LiveEvent getReasonsTorDisabled() { + return reasonsTorDisabled; + } + + private void tryToEnablePlugin(TransportId id) { + if (id.equals(TorConstants.ID)) { + int reasons = viewModel.getReasonsDisabled(id); + if (reasons == 0 || reasons == REASON_USER) { + viewModel.setPluginEnabled(id, true); + } else { + reasonsTorDisabled.setEvent(reasons); + } + } else { + viewModel.setPluginEnabled(id, true); + } + } + private void stateUpdate(TransportId id, State state) { updateIcon(getIcon(id), state); updateSwitch(getSwitch(id), state); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java index 9d1d8da7f..07dd5a327 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java @@ -85,6 +85,7 @@ import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY; import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_RINGTONE; import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.SIGN_OUT_URI; +import static org.briarproject.briar.android.util.UiUtils.getCountryDisplayName; import static org.briarproject.briar.android.util.UiUtils.hasScreenLock; import static org.briarproject.briar.android.util.UiUtils.triggerFeedback; import static org.briarproject.briar.api.android.AndroidNotificationManager.BLOG_CHANNEL_ID; @@ -316,13 +317,8 @@ public class SettingsFragment extends PreferenceFragmentCompat // Look up country name in the user's chosen language if available String country = locationUtils.getCurrentCountry(); - String countryName = country; - for (Locale locale : Locale.getAvailableLocales()) { - if (locale.getCountry().equalsIgnoreCase(country)) { - countryName = locale.getDisplayCountry(); - break; - } - } + String countryName = getCountryDisplayName(country); + boolean blocked = circumventionProvider.isTorProbablyBlocked(country); boolean useBridges = circumventionProvider.doBridgesWork(country); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java index f4bdb966a..6e849600e 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java @@ -38,6 +38,8 @@ import org.briarproject.briar.R; import org.briarproject.briar.android.view.ArticleMovementMethod; import org.briarproject.briar.android.widget.LinkDialogFragment; +import java.util.Locale; + import androidx.annotation.AttrRes; import androidx.annotation.ColorInt; import androidx.annotation.ColorRes; @@ -401,4 +403,14 @@ public class UiUtils { return ctx.getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_RTL; } + + public static String getCountryDisplayName(String isoCode) { + for (Locale locale : Locale.getAvailableLocales()) { + if (locale.getCountry().equalsIgnoreCase(isoCode)) { + return locale.getDisplayCountry(); + } + } + // Name is unknown + return isoCode; + } } diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index 44383379a..504ea3e1b 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -569,6 +569,11 @@ Briar is locked Tap to unlock + + Turning on Tor will change the following settings:\n\n%1$s + Don\'t use mobile data + Don\'t connect to Internet (Tor) in %1$s + From 7b1c6f3fddcf2c52a7b774cdcdcf03d9317859dd Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 10 Feb 2020 11:31:29 +0000 Subject: [PATCH 50/69] Add icon, title to Change Settings dialog. --- .../briar/android/navdrawer/NavDrawerActivity.java | 7 +++++-- briar-android/src/main/res/values/strings.xml | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java index 91b2e147c..3b938e686 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java @@ -390,9 +390,12 @@ public class NavDrawerActivity extends BriarActivity implements AlertDialog.Builder b = new AlertDialog.Builder(this, R.style.BriarDialogTheme); + b.setTitle(R.string.tor_override_settings_title); + b.setIcon(R.drawable.ic_settings_black_24dp); b.setMessage(message); - b.setPositiveButton(R.string.continue_button, (dialog, which) -> - viewModel.setTorEnabled(battery, mobileData, location)); + b.setPositiveButton(R.string.tor_override_settings_confirm, + (dialog, which) -> + viewModel.setTorEnabled(battery, mobileData, location)); b.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); b.show(); diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index 504ea3e1b..a67955b1f 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -570,9 +570,11 @@ Tap to unlock + Change Settings Turning on Tor will change the following settings:\n\n%1$s Don\'t use mobile data Don\'t connect to Internet (Tor) in %1$s + Change From e9ec5734e2439a4cbf2f22395e3cb9dbca0632ed Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 10 Feb 2020 11:15:12 +0000 Subject: [PATCH 51/69] Show dialog from controller. --- .../android/navdrawer/NavDrawerActivity.java | 52 +--------------- .../android/navdrawer/NavDrawerViewModel.java | 9 ++- .../navdrawer/PluginViewController.java | 62 +++++++++++++++---- 3 files changed, 58 insertions(+), 65 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java index 3b938e686..0ea593c9e 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java @@ -17,7 +17,6 @@ import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; -import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BriarActivity; @@ -38,7 +37,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBarDrawerToggle; -import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.Toolbar; import androidx.core.app.ActivityCompat; import androidx.drawerlayout.widget.DrawerLayout; @@ -55,13 +53,9 @@ import static androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE; import static java.util.Objects.requireNonNull; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING; -import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY; -import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED; -import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA; import static org.briarproject.briar.android.BriarService.EXTRA_STARTUP_FAILED; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD; import static org.briarproject.briar.android.navdrawer.IntentRouter.handleExternalIntent; -import static org.briarproject.briar.android.util.UiUtils.getCountryDisplayName; import static org.briarproject.briar.android.util.UiUtils.getDaysUntilExpiry; @MethodsNotNullByDefault @@ -94,9 +88,6 @@ public class NavDrawerActivity extends BriarActivity implements @Inject LifecycleManager lifecycleManager; - @Inject - LocationUtils locationUtils; - private DrawerLayout drawerLayout; private NavigationView navigation; @@ -120,10 +111,7 @@ public class NavDrawerActivity extends BriarActivity implements }); View drawerScrollView = findViewById(R.id.drawerScrollView); - PluginViewController pluginViewController = - new PluginViewController(drawerScrollView, this, viewModel); - pluginViewController.getReasonsTorDisabled().observeEvent(this, - this::showTorSettingsDialog); + new PluginViewController(drawerScrollView, this, viewModel); Toolbar toolbar = findViewById(R.id.toolbar); drawerLayout = findViewById(R.id.drawer_layout); @@ -362,42 +350,4 @@ public class NavDrawerActivity extends BriarActivity implements expiryWarning.setVisibility(GONE); } } - - private void showTorSettingsDialog(int reasonsDisabled) { - boolean battery = (reasonsDisabled & REASON_BATTERY) != 0; - boolean mobileData = (reasonsDisabled & REASON_MOBILE_DATA) != 0; - boolean location = (reasonsDisabled & REASON_COUNTRY_BLOCKED) != 0; - - StringBuilder s = new StringBuilder(); - if (location) { - s.append("\t\u2022 "); - s.append(getString(R.string.tor_override_network_setting, - getCountryDisplayName(locationUtils.getCurrentCountry()))); - s.append('\n'); - } - if (mobileData) { - s.append("\t\u2022 "); - s.append(getString(R.string.tor_override_mobile_data_setting)); - s.append('\n'); - } - if (battery) { - s.append("\t\u2022 "); - s.append(getString(R.string.tor_only_when_charging_title)); - s.append('\n'); - } - String message = - getString(R.string.tor_override_settings_body, s.toString()); - - AlertDialog.Builder b = - new AlertDialog.Builder(this, R.style.BriarDialogTheme); - b.setTitle(R.string.tor_override_settings_title); - b.setIcon(R.drawable.ic_settings_black_24dp); - b.setMessage(message); - b.setPositiveButton(R.string.tor_override_settings_confirm, - (dialog, which) -> - viewModel.setTorEnabled(battery, mobileData, location)); - b.setNegativeButton(R.string.cancel, (dialog, which) -> - dialog.dismiss()); - b.show(); - } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java index 8f1909541..ac47a092a 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java @@ -18,6 +18,7 @@ import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.event.TransportStateEvent; import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.SettingsManager; +import org.briarproject.bramble.api.system.LocationUtils; import java.util.concurrent.Executor; import java.util.logging.Logger; @@ -45,6 +46,7 @@ import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE; import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD; import static org.briarproject.briar.android.controller.BriarControllerImpl.DOZE_ASK_AGAIN; import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE; +import static org.briarproject.briar.android.util.UiUtils.getCountryDisplayName; import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting; @NotNullByDefault @@ -62,6 +64,7 @@ public class NavDrawerViewModel extends AndroidViewModel private final Executor dbExecutor; private final SettingsManager settingsManager; private final PluginManager pluginManager; + private final LocationUtils locationUtils; private final EventBus eventBus; private final MutableLiveData showExpiryWarning = @@ -79,11 +82,12 @@ public class NavDrawerViewModel extends AndroidViewModel @Inject NavDrawerViewModel(Application app, @DatabaseExecutor Executor dbExecutor, SettingsManager settingsManager, PluginManager pluginManager, - EventBus eventBus) { + LocationUtils locationUtils, EventBus eventBus) { super(app); this.dbExecutor = dbExecutor; this.settingsManager = settingsManager; this.pluginManager = pluginManager; + this.locationUtils = locationUtils; this.eventBus = eventBus; eventBus.addListener(this); updatePluginStates(); @@ -248,4 +252,7 @@ public class NavDrawerViewModel extends AndroidViewModel }); } + String getCurrentCountryName() { + return getCountryDisplayName(locationUtils.getCurrentCountry()); + } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java index ea3de6d2d..828cdf0a3 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java @@ -10,14 +10,13 @@ import org.briarproject.bramble.api.plugin.Plugin.State; import org.briarproject.bramble.api.plugin.TorConstants; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.briar.R; -import org.briarproject.briar.android.viewmodel.LiveEvent; -import org.briarproject.briar.android.viewmodel.MutableLiveEvent; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.AppCompatImageButton; import androidx.appcompat.widget.SwitchCompat; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.ConstraintSet; -import androidx.lifecycle.LifecycleOwner; import static android.os.Build.VERSION.SDK_INT; import static android.transition.TransitionManager.beginDelayedTransition; @@ -28,23 +27,26 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING; import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; +import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY; +import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED; +import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA; import static org.briarproject.briar.android.navdrawer.NavDrawerViewModel.TRANSPORT_IDS; class PluginViewController { + private final AppCompatActivity activity; private final NavDrawerViewModel viewModel; private final ConstraintLayout drawerContent; private final ConstraintSet collapsedConstraints, expandedConstraints; private final AppCompatImageButton chevronView; private final ImageView torIcon, wifiIcon, btIcon; private final SwitchCompat torSwitch, wifiSwitch, btSwitch; - private final MutableLiveEvent reasonsTorDisabled = - new MutableLiveEvent<>(); private boolean expanded = false; - PluginViewController(View v, LifecycleOwner owner, + PluginViewController(View v, AppCompatActivity activity, NavDrawerViewModel viewModel) { + this.activity = activity; this.viewModel = viewModel; drawerContent = v.findViewById(R.id.drawerContent); @@ -88,8 +90,8 @@ class PluginViewController { // changes its state switchCompat.toggle(); }); - viewModel.getPluginState(t) - .observe(owner, state -> stateUpdate(t, state)); + viewModel.getPluginState(t).observe(activity, state -> + stateUpdate(t, state)); } } @@ -105,17 +107,13 @@ class PluginViewController { expanded = !expanded; } - LiveEvent getReasonsTorDisabled() { - return reasonsTorDisabled; - } - private void tryToEnablePlugin(TransportId id) { if (id.equals(TorConstants.ID)) { int reasons = viewModel.getReasonsDisabled(id); if (reasons == 0 || reasons == REASON_USER) { viewModel.setPluginEnabled(id, true); } else { - reasonsTorDisabled.setEvent(reasons); + showTorSettingsDialog(reasons); } } else { viewModel.setPluginEnabled(id, true); @@ -160,4 +158,42 @@ class PluginViewController { icon.setColorFilter(color); } + private void showTorSettingsDialog(int reasonsDisabled) { + boolean battery = (reasonsDisabled & REASON_BATTERY) != 0; + boolean mobileData = (reasonsDisabled & REASON_MOBILE_DATA) != 0; + boolean location = (reasonsDisabled & REASON_COUNTRY_BLOCKED) != 0; + + StringBuilder s = new StringBuilder(); + if (location) { + s.append("\t\u2022 "); + s.append(activity.getString(R.string.tor_override_network_setting, + viewModel.getCurrentCountryName())); + s.append('\n'); + } + if (mobileData) { + s.append("\t\u2022 "); + s.append(activity.getString( + R.string.tor_override_mobile_data_setting)); + s.append('\n'); + } + if (battery) { + s.append("\t\u2022 "); + s.append(activity.getString(R.string.tor_only_when_charging_title)); + s.append('\n'); + } + String message = activity.getString( + R.string.tor_override_settings_body, s.toString()); + + AlertDialog.Builder b = + new AlertDialog.Builder(activity, R.style.BriarDialogTheme); + b.setTitle(R.string.tor_override_settings_title); + b.setIcon(R.drawable.ic_settings_black_24dp); + b.setMessage(message); + b.setPositiveButton(R.string.tor_override_settings_confirm, + (dialog, which) -> + viewModel.setTorEnabled(battery, mobileData, location)); + b.setNegativeButton(R.string.cancel, (dialog, which) -> + dialog.dismiss()); + b.show(); + } } From ee19d2f574a9247758d658e3b1cde27d1675c8f9 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Tue, 11 Feb 2020 10:58:32 +0000 Subject: [PATCH 52/69] Use tinted icon for plugin settings dialog. --- .../android/contact/add/remote/NicknameFragment.java | 9 ++------- .../briar/android/conversation/ImageActivity.java | 9 ++------- .../briar/android/navdrawer/PluginViewController.java | 3 ++- .../org/briarproject/briar/android/util/UiUtils.java | 11 +++++++++++ 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/NicknameFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/NicknameFragment.java index 634c45462..aec20a01d 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/NicknameFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/NicknameFragment.java @@ -3,7 +3,6 @@ package org.briarproject.briar.android.contact.add.remote; import android.content.Context; import android.content.DialogInterface.OnClickListener; import android.content.Intent; -import android.graphics.drawable.Drawable; import android.os.Bundle; import android.text.Editable; import android.view.LayoutInflater; @@ -38,12 +37,10 @@ import androidx.lifecycle.ViewModelProviders; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; import static android.widget.Toast.LENGTH_LONG; -import static androidx.core.content.ContextCompat.getColor; -import static androidx.core.content.ContextCompat.getDrawable; -import static androidx.core.graphics.drawable.DrawableCompat.setTint; import static java.util.Objects.requireNonNull; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.util.StringUtils.utf8IsTooLong; +import static org.briarproject.briar.android.util.UiUtils.getDialogIcon; @MethodsNotNullByDefault @ParametersNotNullByDefault @@ -199,9 +196,7 @@ public class NicknameFragment extends BaseFragment { private void showWarningDialog(String name1, String name2) { Context ctx = requireContext(); Builder b = new Builder(ctx, R.style.BriarDialogTheme); - Drawable icon = getDrawable(ctx, R.drawable.alerts_and_states_error); - setTint(requireNonNull(icon), getColor(ctx, R.color.color_primary)); - b.setIcon(icon); + b.setIcon(getDialogIcon(ctx, R.drawable.alerts_and_states_error)); b.setTitle(getString(R.string.duplicate_link_dialog_title)); b.setMessage( getString(R.string.duplicate_link_dialog_text_3, name1, name2)); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageActivity.java index 749ef495c..10d2da314 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageActivity.java @@ -2,7 +2,6 @@ package org.briarproject.briar.android.conversation; import android.content.DialogInterface.OnClickListener; import android.content.Intent; -import android.graphics.drawable.Drawable; import android.os.Bundle; import android.transition.Fade; import android.transition.Transition; @@ -35,8 +34,6 @@ import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AlertDialog.Builder; import androidx.appcompat.widget.Toolbar; -import androidx.core.content.ContextCompat; -import androidx.core.graphics.drawable.DrawableCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentStatePagerAdapter; @@ -59,6 +56,7 @@ import static com.google.android.material.snackbar.Snackbar.LENGTH_LONG; import static java.util.Objects.requireNonNull; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_SAVE_ATTACHMENT; import static org.briarproject.briar.android.util.UiUtils.formatDateAbsolute; +import static org.briarproject.briar.android.util.UiUtils.getDialogIcon; @MethodsNotNullByDefault @ParametersNotNullByDefault @@ -278,10 +276,7 @@ public class ImageActivity extends BriarActivity Builder builder = new Builder(this, R.style.BriarDialogTheme); builder.setTitle(getString(R.string.dialog_title_save_image)); builder.setMessage(getString(R.string.dialog_message_save_image)); - Drawable icon = ContextCompat.getDrawable(this, R.drawable.ic_security); - DrawableCompat.setTint(requireNonNull(icon), - ContextCompat.getColor(this, R.color.color_primary)); - builder.setIcon(icon); + builder.setIcon(getDialogIcon(this, R.drawable.ic_security)); builder.setPositiveButton(R.string.save_image, okListener); builder.setNegativeButton(R.string.cancel, null); builder.show(); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java index 828cdf0a3..3f551b1ee 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java @@ -31,6 +31,7 @@ import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA; import static org.briarproject.briar.android.navdrawer.NavDrawerViewModel.TRANSPORT_IDS; +import static org.briarproject.briar.android.util.UiUtils.getDialogIcon; class PluginViewController { @@ -187,7 +188,7 @@ class PluginViewController { AlertDialog.Builder b = new AlertDialog.Builder(activity, R.style.BriarDialogTheme); b.setTitle(R.string.tor_override_settings_title); - b.setIcon(R.drawable.ic_settings_black_24dp); + b.setIcon(getDialogIcon(activity, R.drawable.ic_settings_black_24dp)); b.setMessage(message); b.setPositiveButton(R.string.tor_override_settings_confirm, (dialog, which) -> diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java index 6e849600e..bf3805232 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java @@ -6,6 +6,7 @@ import android.app.KeyguardManager; import android.content.Context; import android.content.DialogInterface.OnClickListener; import android.content.Intent; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.PowerManager; import android.text.Html; @@ -43,6 +44,7 @@ import java.util.Locale; import androidx.annotation.AttrRes; import androidx.annotation.ColorInt; import androidx.annotation.ColorRes; +import androidx.annotation.DrawableRes; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.annotation.UiThread; @@ -81,7 +83,10 @@ import static androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM; import static androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO; import static androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES; import static androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode; +import static androidx.core.content.ContextCompat.getColor; +import static androidx.core.content.ContextCompat.getDrawable; import static androidx.core.content.ContextCompat.getSystemService; +import static androidx.core.graphics.drawable.DrawableCompat.setTint; import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_RTL; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.DAYS; @@ -413,4 +418,10 @@ public class UiUtils { // Name is unknown return isoCode; } + + public static Drawable getDialogIcon(Context ctx, @DrawableRes int resId) { + Drawable icon = getDrawable(ctx, resId); + setTint(requireNonNull(icon), getColor(ctx, R.color.color_primary)); + return icon; + } } From c2b0a4b8d1a1c83704e27e6aa51595057278ba07 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 17 Feb 2020 11:58:55 +0000 Subject: [PATCH 53/69] Remove unnecessary plugin lookup. --- .../briar/android/navdrawer/NavDrawerViewModel.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java index ac47a092a..394f1b7c3 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java @@ -235,9 +235,6 @@ public class NavDrawerViewModel extends AndroidViewModel } void setTorEnabled(boolean battery, boolean mobileData, boolean location) { - Plugin plugin = pluginManager.getPlugin(TorConstants.ID); - if (plugin == null) return; - Settings s = new Settings(); s.putBoolean(PREF_PLUGIN_ENABLE, true); if (battery) s.putBoolean(PREF_TOR_ONLY_WHEN_CHARGING, false); From 2bd2f67693a0bb5256f179adfed20e7ddf056c52 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 5 Mar 2020 14:37:55 +0000 Subject: [PATCH 54/69] Interrupt sync sessions when transport becomes inactive. This ensures connections are closed when the user disables a transport. --- .../org/briarproject/bramble/sync/DuplexOutgoingSession.java | 4 ++++ .../org/briarproject/bramble/sync/SimplexOutgoingSession.java | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/bramble-core/src/main/java/org/briarproject/bramble/sync/DuplexOutgoingSession.java b/bramble-core/src/main/java/org/briarproject/bramble/sync/DuplexOutgoingSession.java index a74302d62..6eed42511 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/sync/DuplexOutgoingSession.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/sync/DuplexOutgoingSession.java @@ -12,6 +12,7 @@ import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.TransportId; +import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent; import org.briarproject.bramble.api.sync.Ack; import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Offer; @@ -238,6 +239,9 @@ class DuplexOutgoingSession implements SyncSession, EventListener { } else if (e instanceof CloseSyncConnectionsEvent) { CloseSyncConnectionsEvent c = (CloseSyncConnectionsEvent) e; if (c.getTransportId().equals(transportId)) interrupt(); + } else if (e instanceof TransportInactiveEvent) { + TransportInactiveEvent t = (TransportInactiveEvent) e; + if (t.getTransportId().equals(transportId)) interrupt(); } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/sync/SimplexOutgoingSession.java b/bramble-core/src/main/java/org/briarproject/bramble/sync/SimplexOutgoingSession.java index 2697a2cfc..1d32ca4ee 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/sync/SimplexOutgoingSession.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/sync/SimplexOutgoingSession.java @@ -12,6 +12,7 @@ import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.TransportId; +import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent; import org.briarproject.bramble.api.sync.Ack; import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.SyncRecordWriter; @@ -131,6 +132,9 @@ class SimplexOutgoingSession implements SyncSession, EventListener { } else if (e instanceof CloseSyncConnectionsEvent) { CloseSyncConnectionsEvent c = (CloseSyncConnectionsEvent) e; if (c.getTransportId().equals(transportId)) interrupt(); + } else if (e instanceof TransportInactiveEvent) { + TransportInactiveEvent t = (TransportInactiveEvent) e; + if (t.getTransportId().equals(transportId)) interrupt(); } } From e1084ffadd9d70b6d28f8e254523097283c9eee2 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 17 Feb 2020 11:07:14 +0000 Subject: [PATCH 55/69] Support IPv6 SLAAC addresses. --- .../plugin/tcp/AndroidLanTcpPlugin.java | 27 ++- .../bramble/api/plugin/LanTcpConstants.java | 1 + .../bramble/util/PrivacyUtils.java | 52 ++--- .../bramble/plugin/tcp/LanTcpPlugin.java | 114 +++++++++-- .../bramble/plugin/tcp/PortMapperImpl.java | 10 +- .../bramble/plugin/tcp/TcpPlugin.java | 183 +++++++++++------- .../bramble/plugin/tcp/WanTcpPlugin.java | 17 +- .../android/reporting/BriarReportPrimer.java | 20 +- 8 files changed, 279 insertions(+), 145 deletions(-) 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 c3d080918..f82f02ee3 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 @@ -83,7 +83,12 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { } @Override - protected List getUsableLocalInetAddresses() { + protected List getUsableLocalInetAddresses(boolean ipv4) { + if (ipv4) return getUsableLocalIpv4Addresses(); + else return getUsableLocalIpv6Addresses(); + } + + private List getUsableLocalIpv4Addresses() { // If the device doesn't have wifi, don't open any sockets if (wifiManager == null) return emptyList(); // If we're connected to a wifi network, return its address @@ -100,6 +105,17 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { return emptyList(); } + private List getUsableLocalIpv6Addresses() { + // If the device doesn't have wifi, don't open any sockets + if (wifiManager == null) return emptyList(); + // If we have a SLAAC address, return it + for (InetAddress addr : getLocalInetAddresses()) { + if (isSlaacAddress(addr)) return singletonList(addr); + } + // No suitable addresses + return emptyList(); + } + private InetAddress intToInetAddress(int ip) { byte[] ipBytes = new byte[4]; ipBytes[0] = (byte) (ip & 0xFF); @@ -150,12 +166,13 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { } else if (addrs.isEmpty()) { LOG.info("Not connected to wifi"); socketFactory = SocketFactory.getDefault(); - // Server socket may not have been closed automatically when - // interface was taken down. Socket will be cleared and state + // Server sockets may not have been closed automatically when + // interface was taken down. Sockets will be cleared and state // updated in acceptContactConnections() if (s == ACTIVE) { - LOG.info("Closing server socket"); - tryToClose(state.getServerSocket(), LOG, WARNING); + LOG.info("Closing server sockets"); + tryToClose(state.getServerSocket(true), LOG, WARNING); + tryToClose(state.getServerSocket(false), LOG, WARNING); } } else { LOG.info("Connected to wifi"); diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/LanTcpConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/LanTcpConstants.java index d546a60cd..835d67b5a 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/LanTcpConstants.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/LanTcpConstants.java @@ -7,6 +7,7 @@ public interface LanTcpConstants { // Transport properties (shared with contacts) String PROP_IP_PORTS = "ipPorts"; String PROP_PORT = "port"; + String PROP_SLAAC = "slaac"; // A local setting String PREF_LAN_IP_PORTS = "ipPorts"; diff --git a/bramble-api/src/main/java/org/briarproject/bramble/util/PrivacyUtils.java b/bramble-api/src/main/java/org/briarproject/bramble/util/PrivacyUtils.java index 9156b9caa..10df6686c 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/util/PrivacyUtils.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/util/PrivacyUtils.java @@ -2,13 +2,17 @@ package org.briarproject.bramble.util; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; -import java.net.Inet6Address; +import java.net.Inet4Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import javax.annotation.Nullable; +import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; +import static org.briarproject.bramble.util.StringUtils.isValidMac; +import static org.briarproject.bramble.util.StringUtils.toHexString; + @NotNullByDefault public class PrivacyUtils { @@ -19,7 +23,7 @@ public class PrivacyUtils { @Nullable public static String scrubMacAddress(@Nullable String address) { - if (address == null || address.length() == 0) return null; + if (isNullOrEmpty(address) || !isValidMac(address)) return address; // this is a fake address we need to know about if (address.equals("02:00:00:00:00:00")) return address; // keep first and last octet of MAC address @@ -27,39 +31,37 @@ public class PrivacyUtils { + address.substring(14, 17); } - @Nullable public static String scrubInetAddress(InetAddress address) { - // don't scrub link and site local addresses - if (address.isLinkLocalAddress() || address.isSiteLocalAddress()) - return address.toString(); - // completely scrub IPv6 addresses - if (address instanceof Inet6Address) return "[scrubbed]"; - // keep first and last octet of IPv4 addresses - return scrubInetAddress(address.toString()); + if (address instanceof Inet4Address) { + // Don't scrub local IPv4 addresses + if (address.isLoopbackAddress() || address.isLinkLocalAddress() || + address.isSiteLocalAddress()) { + return address.getHostAddress(); + } + // Keep first and last octet of non-local IPv4 addresses + return scrubIpv4Address(address.getAddress()); + } else { + // Keep first and last octet of IPv6 addresses + return scrubIpv6Address(address.getAddress()); + } } - @Nullable - public static String scrubInetAddress(@Nullable String address) { - if (address == null) return null; - - int firstDot = address.indexOf("."); - if (firstDot == -1) return "[scrubbed]"; - String prefix = address.substring(0, firstDot + 1); - int lastDot = address.lastIndexOf("."); - String suffix = address.substring(lastDot, address.length()); - return prefix + "[scrubbed]" + suffix; + private static String scrubIpv4Address(byte[] ipv4) { + return (ipv4[0] & 0xFF) + ".[scrubbed]." + (ipv4[3] & 0xFF); + } + + private static String scrubIpv6Address(byte[] ipv6) { + String hex = toHexString(ipv6).toLowerCase(); + return hex.substring(0, 2) + "[scrubbed]" + hex.substring(30); } - @Nullable public static String scrubSocketAddress(InetSocketAddress address) { - InetAddress inetAddress = address.getAddress(); - return scrubInetAddress(inetAddress); + return scrubInetAddress(address.getAddress()); } - @Nullable public static String scrubSocketAddress(SocketAddress address) { if (address instanceof InetSocketAddress) return scrubSocketAddress((InetSocketAddress) address); - return scrubInetAddress(address.toString()); + return "[scrubbed]"; } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java index 1200b10b1..b3c583728 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java @@ -14,6 +14,7 @@ import org.briarproject.bramble.api.settings.Settings; import java.io.IOException; import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.InterfaceAddress; @@ -22,7 +23,6 @@ import java.net.Socket; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; -import java.util.Random; import java.util.concurrent.Executor; import java.util.logging.Logger; @@ -30,6 +30,8 @@ import javax.annotation.Nullable; import static java.lang.Integer.parseInt; import static java.util.Collections.addAll; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; import static java.util.Collections.sort; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; @@ -39,11 +41,14 @@ import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID; import static org.briarproject.bramble.api.plugin.LanTcpConstants.PREF_LAN_IP_PORTS; import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_IP_PORTS; import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_PORT; +import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_SLAAC; import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED; import static org.briarproject.bramble.util.IoUtils.tryToClose; import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress; +import static org.briarproject.bramble.util.StringUtils.fromHexString; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; import static org.briarproject.bramble.util.StringUtils.join; +import static org.briarproject.bramble.util.StringUtils.toHexString; @NotNullByDefault class LanTcpPlugin extends TcpPlugin { @@ -53,6 +58,13 @@ class LanTcpPlugin extends TcpPlugin { private static final int MAX_ADDRESSES = 4; private static final String SEPARATOR = ","; + /** + * The network prefix of a SLAAC IPv6 address. See + * https://tools.ietf.org/html/rfc4862#section-5.3 + */ + private static final byte[] SLAAC_PREFIX = + new byte[] {(byte) 0xFE, (byte) 0x80, 0, 0, 0, 0, 0, 0}; + /** * The IP address of an Android device providing a wifi access point. */ @@ -99,22 +111,22 @@ class LanTcpPlugin extends TcpPlugin { protected void initialisePortProperty() { TransportProperties p = callback.getLocalProperties(); if (isNullOrEmpty(p.get(PROP_PORT))) { - int port = new Random().nextInt(32768) + 32768; + int port = chooseEphemeralPort(); p.put(PROP_PORT, String.valueOf(port)); callback.mergeLocalProperties(p); } } @Override - protected List getLocalSocketAddresses() { + protected List getLocalSocketAddresses(boolean ipv4) { TransportProperties p = callback.getLocalProperties(); int preferredPort = parsePortProperty(p.get(PROP_PORT)); String oldIpPorts = p.get(PROP_IP_PORTS); - List olds = parseSocketAddresses(oldIpPorts); + List olds = parseIpv4SocketAddresses(oldIpPorts); List locals = new ArrayList<>(); List fallbacks = new ArrayList<>(); - for (InetAddress local : getUsableLocalInetAddresses()) { + for (InetAddress local : getUsableLocalInetAddresses(ipv4)) { // If we've used this address before, try to use the same port int port = preferredPort; for (InetSocketAddress old : olds) { @@ -140,17 +152,17 @@ class LanTcpPlugin extends TcpPlugin { } } - private List parseSocketAddresses(String ipPorts) { + private List parseIpv4SocketAddresses(String ipPorts) { List addresses = new ArrayList<>(); if (isNullOrEmpty(ipPorts)) return addresses; for (String ipPort : ipPorts.split(SEPARATOR)) { - InetSocketAddress a = parseSocketAddress(ipPort); + InetSocketAddress a = parseIpv4SocketAddress(ipPort); if (a != null) addresses.add(a); } return addresses; } - protected List getUsableLocalInetAddresses() { + protected List getUsableLocalInetAddresses(boolean ipv4) { List ifAddrs = new ArrayList<>(getLocalInterfaceAddresses()); // Prefer longer network prefixes @@ -159,13 +171,18 @@ class LanTcpPlugin extends TcpPlugin { List addrs = new ArrayList<>(); for (InterfaceAddress ifAddr : ifAddrs) { InetAddress addr = ifAddr.getAddress(); - if (isAcceptableAddress(addr)) addrs.add(addr); + if (isAcceptableAddress(addr, ipv4)) addrs.add(addr); } return addrs; } @Override - protected void setLocalSocketAddress(InetSocketAddress a) { + protected void setLocalSocketAddress(InetSocketAddress a, boolean ipv4) { + if (ipv4) setLocalIpv4SocketAddress(a); + else setLocalIpv6SocketAddress(a); + } + + private void setLocalIpv4SocketAddress(InetSocketAddress a) { String ipPort = getIpPortString(a); // Get the list of recently used addresses String setting = callback.getSettings().get(PREF_LAN_IP_PORTS); @@ -198,11 +215,38 @@ class LanTcpPlugin extends TcpPlugin { callback.mergeSettings(settings); } + private void setLocalIpv6SocketAddress(InetSocketAddress a) { + if (isSlaacAddress(a.getAddress())) { + String property = toHexString(a.getAddress().getAddress()); + TransportProperties properties = new TransportProperties(); + properties.put(PROP_SLAAC, property); + callback.mergeLocalProperties(properties); + } + } + + // See https://tools.ietf.org/html/rfc4862#section-5.3 + protected boolean isSlaacAddress(InetAddress a) { + if (!(a instanceof Inet6Address)) return false; + byte[] ip = a.getAddress(); + for (int i = 0; i < 8; i++) { + if (ip[i] != SLAAC_PREFIX[i]) return false; + } + return (ip[8] & 0x02) == 0x02 + && ip[11] == (byte) 0xFF + && ip[12] == (byte) 0xFE; + } + @Override protected List getRemoteSocketAddresses( + TransportProperties p, boolean ipv4) { + if (ipv4) return getRemoteIpv4SocketAddresses(p); + else return getRemoteIpv6SocketAddresses(p); + } + + private List getRemoteIpv4SocketAddresses( TransportProperties p) { String ipPorts = p.get(PROP_IP_PORTS); - List remotes = parseSocketAddresses(ipPorts); + List remotes = parseIpv4SocketAddresses(ipPorts); int port = parsePortProperty(p.get(PROP_PORT)); // If the contact has a preferred port, we can guess their IP:port when // they're providing a wifi access point @@ -217,20 +261,48 @@ class LanTcpPlugin extends TcpPlugin { return remotes; } - private boolean isAcceptableAddress(InetAddress a) { - // Accept link-local and site-local IPv4 addresses - boolean ipv4 = a instanceof Inet4Address; - boolean loop = a.isLoopbackAddress(); - boolean link = a.isLinkLocalAddress(); - boolean site = a.isSiteLocalAddress(); - return ipv4 && !loop && (link || site); + private List getRemoteIpv6SocketAddresses( + TransportProperties p) { + InetAddress slaac = parseSlaacProperty(p.get(PROP_SLAAC)); + int port = parsePortProperty(p.get(PROP_PORT)); + if (slaac == null || port == 0) return emptyList(); + return singletonList(new InetSocketAddress(slaac, port)); + } + + @Nullable + private InetAddress parseSlaacProperty(String slaacProperty) { + if (isNullOrEmpty(slaacProperty)) return null; + try { + byte[] ip = fromHexString(slaacProperty); + if (ip.length != 16) return null; + InetAddress a = InetAddress.getByAddress(ip); + return isSlaacAddress(a) ? a : null; + } catch (IllegalArgumentException | UnknownHostException e) { + return null; + } + } + + private boolean isAcceptableAddress(InetAddress a, boolean ipv4) { + if (ipv4) { + // Accept link-local and site-local IPv4 addresses + boolean isIpv4 = a instanceof Inet4Address; + boolean loop = a.isLoopbackAddress(); + boolean link = a.isLinkLocalAddress(); + boolean site = a.isSiteLocalAddress(); + return isIpv4 && !loop && (link || site); + } else { + // Accept IPv6 SLAAC addresses + return isSlaacAddress(a); + } } @Override protected boolean isConnectable(InterfaceAddress local, InetSocketAddress remote) { if (remote.getPort() == 0) return false; - if (!isAcceptableAddress(remote.getAddress())) return false; + InetAddress remoteAddress = remote.getAddress(); + boolean ipv4 = local.getAddress() instanceof Inet4Address; + if (!isAcceptableAddress(remoteAddress, ipv4)) return false; // Try to determine whether the address is on the same LAN as us byte[] localIp = local.getAddress().getAddress(); byte[] remoteIp = remote.getAddress().getAddress(); @@ -262,7 +334,7 @@ class LanTcpPlugin extends TcpPlugin { @Override public KeyAgreementListener createKeyAgreementListener(byte[] commitment) { ServerSocket ss = null; - for (InetSocketAddress addr : getLocalSocketAddresses()) { + for (InetSocketAddress addr : getLocalSocketAddresses(true)) { // Don't try to reuse the same port we use for contact connections addr = new InetSocketAddress(addr.getAddress(), 0); try { @@ -291,7 +363,7 @@ class LanTcpPlugin extends TcpPlugin { @Override public DuplexTransportConnection createKeyAgreementConnection( byte[] commitment, BdfList descriptor) { - ServerSocket ss = state.getServerSocket(); + ServerSocket ss = state.getServerSocket(true); if (ss == null) return null; InterfaceAddress local = getLocalInterfaceAddress(ss.getInetAddress()); if (local == null) { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/PortMapperImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/PortMapperImpl.java index 584510705..8b5adc876 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/PortMapperImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/PortMapperImpl.java @@ -54,11 +54,13 @@ class PortMapperImpl implements PortMapper { shutdownManager.addShutdownHook(() -> deleteMapping(port)); } String externalString = gateway.getExternalIPAddress(); - if (LOG.isLoggable(INFO)) - LOG.info( - "External address " + scrubInetAddress(externalString)); - if (externalString != null) + if (externalString == null) { + LOG.info("External address not available"); + } else { external = InetAddress.getByName(externalString); + if (LOG.isLoggable(INFO)) + LOG.info("External address " + scrubInetAddress(external)); + } } catch (IOException | SAXException e) { logException(LOG, WARNING, e); } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java index 4a8ea8753..954868367 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java @@ -43,6 +43,7 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.ThreadSafe; +import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.list; import static java.util.logging.Level.INFO; @@ -78,20 +79,22 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { * Returns zero or more socket addresses on which the plugin should listen, * in order of preference. At most one of the addresses will be bound. */ - protected abstract List getLocalSocketAddresses(); + protected abstract List getLocalSocketAddresses( + boolean ipv4); /** * Adds the address on which the plugin is listening to the transport * properties. */ - protected abstract void setLocalSocketAddress(InetSocketAddress a); + protected abstract void setLocalSocketAddress(InetSocketAddress a, + boolean ipv4); /** * Returns zero or more socket addresses for connecting to a contact with * the given transport properties. */ protected abstract List getRemoteSocketAddresses( - TransportProperties p); + TransportProperties p, boolean ipv4); /** * Returns true if connections to the given address can be attempted. @@ -136,37 +139,43 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { protected void bind() { bindExecutor.execute(() -> { if (getState() != INACTIVE) return; - ServerSocket ss = null; - for (InetSocketAddress addr : getLocalSocketAddresses()) { - try { - ss = new ServerSocket(); - ss.bind(addr); - break; - } catch (IOException e) { - if (LOG.isLoggable(INFO)) - LOG.info("Failed to bind " + scrubSocketAddress(addr)); - tryToClose(ss, LOG, WARNING); - } - } - if (ss == null) { - LOG.info("Could not bind server socket"); - return; - } - if (!state.setServerSocket(ss)) { - LOG.info("Closing redundant server socket"); - tryToClose(ss, LOG, WARNING); - return; - } - backoff.reset(); - InetSocketAddress local = - (InetSocketAddress) ss.getLocalSocketAddress(); - setLocalSocketAddress(local); - if (LOG.isLoggable(INFO)) - LOG.info("Listening on " + scrubSocketAddress(local)); - acceptContactConnections(ss); + bind(true); + bind(false); }); } + private void bind(boolean ipv4) { + ServerSocket ss = null; + for (InetSocketAddress addr : getLocalSocketAddresses(ipv4)) { + try { + ss = new ServerSocket(); + ss.bind(addr); + break; + } catch (IOException e) { + if (LOG.isLoggable(INFO)) + LOG.info("Failed to bind " + scrubSocketAddress(addr)); + tryToClose(ss, LOG, WARNING); + } + } + if (ss == null) { + LOG.info("Could not bind server socket"); + return; + } + if (!state.setServerSocket(ss, ipv4)) { + LOG.info("Closing redundant server socket"); + tryToClose(ss, LOG, WARNING); + return; + } + backoff.reset(); + InetSocketAddress local = + (InetSocketAddress) ss.getLocalSocketAddress(); + setLocalSocketAddress(local, ipv4); + if (LOG.isLoggable(INFO)) + LOG.info("Listening on " + scrubSocketAddress(local)); + ServerSocket finalSocket = ss; + ioExecutor.execute(() -> acceptContactConnections(finalSocket, ipv4)); + } + String getIpPortString(InetSocketAddress a) { String addr = a.getAddress().getHostAddress(); int percent = addr.indexOf('%'); @@ -174,7 +183,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { return addr + ":" + a.getPort(); } - private void acceptContactConnections(ServerSocket ss) { + private void acceptContactConnections(ServerSocket ss, boolean ipv4) { while (true) { Socket s; try { @@ -183,12 +192,13 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { } catch (IOException e) { // This is expected when the server socket is closed LOG.info("Server socket closed"); - state.clearServerSocket(ss); + state.clearServerSocket(ss, ipv4); return; } - if (LOG.isLoggable(INFO)) + if (LOG.isLoggable(INFO)) { LOG.info("Connection from " + scrubSocketAddress(s.getRemoteSocketAddress())); + } backoff.reset(); callback.handleConnection(new TcpTransportConnection(this, s)); } @@ -196,8 +206,9 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { @Override public void stop() { - ServerSocket ss = state.setStopped(); - tryToClose(ss, LOG, WARNING); + for (@Nullable ServerSocket ss : state.setStopped()) { + tryToClose(ss, LOG, WARNING); + } } @Override @@ -242,14 +253,22 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { @Override public DuplexTransportConnection createConnection(TransportProperties p) { - ServerSocket ss = state.getServerSocket(); + DuplexTransportConnection c = createConnection(p, true); + if (c != null) return c; + return createConnection(p, false); + } + + @Nullable + private DuplexTransportConnection createConnection(TransportProperties p, + boolean ipv4) { + ServerSocket ss = state.getServerSocket(ipv4); if (ss == null) return null; InterfaceAddress local = getLocalInterfaceAddress(ss.getInetAddress()); if (local == null) { LOG.warning("No interface for server socket"); return null; } - for (InetSocketAddress remote : getRemoteSocketAddresses(p)) { + for (InetSocketAddress remote : getRemoteSocketAddresses(p, ipv4)) { // Don't try to connect to our own address if (!canConnectToOwnAddress() && remote.getAddress().equals(ss.getInetAddress())) { @@ -274,9 +293,10 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { LOG.info("Connected to " + scrubSocketAddress(remote)); return new TcpTransportConnection(this, s); } catch (IOException e) { - if (LOG.isLoggable(INFO)) + if (LOG.isLoggable(INFO)) { LOG.info("Could not connect to " + scrubSocketAddress(remote)); + } } } return null; @@ -299,8 +319,12 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { return new Socket(); } + int chooseEphemeralPort() { + return 32768 + (int) (Math.random() * 32768); + } + @Nullable - InetSocketAddress parseSocketAddress(String ipPort) { + InetSocketAddress parseIpv4SocketAddress(String ipPort) { if (isNullOrEmpty(ipPort)) return null; String[] split = ipPort.split(":"); if (split.length != 2) return null; @@ -311,14 +335,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { InetAddress a = InetAddress.getByName(addr); int p = Integer.parseInt(port); return new InetSocketAddress(a, p); - } catch (UnknownHostException e) { - if (LOG.isLoggable(WARNING)) - // not scrubbing to enable us to find the problem - LOG.warning("Invalid address: " + addr); - return null; - } catch (NumberFormatException e) { - if (LOG.isLoggable(WARNING)) - LOG.warning("Invalid port: " + port); + } catch (UnknownHostException | NumberFormatException e) { return null; } } @@ -389,13 +406,15 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { @IoExecutor private void onSettingsUpdated(Settings settings) { boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE, false); - ServerSocket ss = state.setEnabledByUser(enabledByUser); + List toClose = state.setEnabledByUser(enabledByUser); State s = getState(); - if (ss != null) { - LOG.info("Disabled by user, closing server socket"); - tryToClose(ss, LOG, WARNING); + if (!toClose.isEmpty()) { + LOG.info("Disabled by user, closing server sockets"); + for (@Nullable ServerSocket ss : toClose) { + tryToClose(ss, LOG, WARNING); + } } else if (s == INACTIVE) { - LOG.info("Enabled by user, opening server socket"); + LOG.info("Enabled by user, opening server sockets"); bind(); } } @@ -409,7 +428,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { @GuardedBy("this") @Nullable - private ServerSocket serverSocket = null; + private ServerSocket serverSocketV4 = null, serverSocketV6 = null; synchronized void setStarted(boolean enabledByUser) { started = true; @@ -417,48 +436,62 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { callback.pluginStateChanged(getState()); } - @Nullable - synchronized ServerSocket setStopped() { + synchronized List setStopped() { stopped = true; - ServerSocket ss = serverSocket; - serverSocket = null; + List toClose = clearServerSockets(); callback.pluginStateChanged(getState()); - return ss; + return toClose; } - @Nullable - synchronized ServerSocket setEnabledByUser(boolean enabledByUser) { + @GuardedBy("this") + private List clearServerSockets() { + List toClose = asList(serverSocketV4, serverSocketV6); + serverSocketV4 = null; + serverSocketV6 = null; + return toClose; + } + + synchronized List setEnabledByUser( + boolean enabledByUser) { this.enabledByUser = enabledByUser; - ServerSocket ss = null; - if (!enabledByUser) { - ss = serverSocket; - serverSocket = null; - } + List toClose = enabledByUser + ? emptyList() : clearServerSockets(); callback.pluginStateChanged(getState()); - return ss; + return toClose; } @Nullable - synchronized ServerSocket getServerSocket() { - return serverSocket; + synchronized ServerSocket getServerSocket(boolean ipv4) { + return ipv4 ? serverSocketV4 : serverSocketV6; } - synchronized boolean setServerSocket(ServerSocket ss) { - if (stopped || serverSocket != null) return false; - serverSocket = ss; + synchronized boolean setServerSocket(ServerSocket ss, boolean ipv4) { + if (stopped) return false; + if (ipv4) { + if (serverSocketV4 != null) return false; + serverSocketV4 = ss; + } else { + if (serverSocketV6 != null) return false; + serverSocketV6 = ss; + } callback.pluginStateChanged(getState()); return true; } - synchronized void clearServerSocket(ServerSocket ss) { - if (serverSocket == ss) serverSocket = null; + synchronized void clearServerSocket(ServerSocket ss, boolean ipv4) { + if (ipv4) { + if (serverSocketV4 == ss) serverSocketV4 = null; + } else { + if (serverSocketV6 == ss) serverSocketV6 = null; + } callback.pluginStateChanged(getState()); } synchronized State getState() { if (!started || stopped) return STARTING_STOPPING; if (!enabledByUser) return DISABLED; - return serverSocket == null ? INACTIVE : ACTIVE; + if (serverSocketV4 != null || serverSocketV6 != null) return ACTIVE; + return INACTIVE; } synchronized int getReasonsDisabled() { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/WanTcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/WanTcpPlugin.java index 0f4643cf1..42f686ebc 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/WanTcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/WanTcpPlugin.java @@ -43,10 +43,11 @@ class WanTcpPlugin extends TcpPlugin { } @Override - protected List getLocalSocketAddresses() { + protected List getLocalSocketAddresses(boolean ipv4) { + if (!ipv4) return emptyList(); // Use the same address and port as last time if available TransportProperties p = callback.getLocalProperties(); - InetSocketAddress old = parseSocketAddress(p.get(PROP_IP_PORT)); + InetSocketAddress old = parseIpv4SocketAddress(p.get(PROP_IP_PORT)); List addrs = new LinkedList<>(); for (InetAddress a : getLocalInetAddresses()) { if (isAcceptableAddress(a)) { @@ -76,14 +77,11 @@ class WanTcpPlugin extends TcpPlugin { return ipv4 && !loop && !link && !site; } - private int chooseEphemeralPort() { - return 32768 + (int) (Math.random() * 32768); - } - @Override protected List getRemoteSocketAddresses( - TransportProperties p) { - InetSocketAddress parsed = parseSocketAddress(p.get(PROP_IP_PORT)); + TransportProperties p, boolean ipv4) { + if (!ipv4) return emptyList(); + InetSocketAddress parsed = parseIpv4SocketAddress(p.get(PROP_IP_PORT)); if (parsed == null) return emptyList(); return singletonList(parsed); } @@ -96,7 +94,8 @@ class WanTcpPlugin extends TcpPlugin { } @Override - protected void setLocalSocketAddress(InetSocketAddress a) { + protected void setLocalSocketAddress(InetSocketAddress a, boolean ipv4) { + if (!ipv4) throw new AssertionError(); if (mappingResult != null && mappingResult.isUsable()) { // Advertise the external address to contacts if (a.equals(mappingResult.getInternal())) { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportPrimer.java b/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportPrimer.java index 253c3a94f..8c5e3e6e3 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportPrimer.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportPrimer.java @@ -21,6 +21,8 @@ import org.briarproject.briar.android.logging.BriefLogFormatter; import java.io.File; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.Callable; @@ -185,12 +187,18 @@ public class BriarReportPrimer implements ReportPrimer { WifiInfo wifiInfo = wm.getConnectionInfo(); if (wifiInfo != null) { int ip = wifiInfo.getIpAddress(); // Nice API, Google - int ip1 = ip & 0xFF; - int ip2 = (ip >> 8) & 0xFF; - int ip3 = (ip >> 16) & 0xFF; - int ip4 = (ip >> 24) & 0xFF; - String address = ip1 + "." + ip2 + "." + ip3 + "." + ip4; - customData.put("Wi-Fi address", scrubInetAddress(address)); + byte[] ipBytes = new byte[4]; + ipBytes[0] = (byte) (ip & 0xFF); + ipBytes[1] = (byte) ((ip >> 8) & 0xFF); + ipBytes[2] = (byte) ((ip >> 16) & 0xFF); + ipBytes[3] = (byte) ((ip >> 24) & 0xFF); + try { + InetAddress address = InetAddress.getByAddress(ipBytes); + customData.put("Wi-Fi address", + scrubInetAddress(address)); + } catch (UnknownHostException ignored) { + // Should only be thrown if address has illegal length + } } } From d0c53f1310c10fa8531207857b3f4e02361e40ca Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 6 Mar 2020 11:24:12 +0000 Subject: [PATCH 56/69] Only bind to the SLAAC address of the wifi interface. This is just a precaution - on all devices I've tested, the wifi interface is the only one with a SLAAC address. --- .../plugin/tcp/AndroidLanTcpPlugin.java | 95 ++++++++++++------- 1 file changed, 61 insertions(+), 34 deletions(-) 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 f82f02ee3..bb5bd6e26 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 @@ -8,6 +8,7 @@ import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import org.briarproject.bramble.PoliteExecutor; +import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.network.event.NetworkStatusEvent; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; @@ -17,7 +18,9 @@ import org.briarproject.bramble.api.settings.Settings; import java.io.IOException; import java.net.InetAddress; +import java.net.NetworkInterface; import java.net.Socket; +import java.net.SocketException; import java.net.UnknownHostException; import java.util.List; import java.util.concurrent.Executor; @@ -31,12 +34,14 @@ import static android.content.Context.WIFI_SERVICE; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.os.Build.VERSION.SDK_INT; import static java.util.Collections.emptyList; +import static java.util.Collections.list; import static java.util.Collections.singletonList; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; import static org.briarproject.bramble.util.IoUtils.tryToClose; +import static org.briarproject.bramble.util.LogUtils.logException; @NotNullByDefault class AndroidLanTcpPlugin extends LanTcpPlugin { @@ -84,36 +89,58 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { @Override protected List getUsableLocalInetAddresses(boolean ipv4) { - if (ipv4) return getUsableLocalIpv4Addresses(); - else return getUsableLocalIpv6Addresses(); + Pair wifi = getWifiIpv4Address(); + if (wifi == null) return emptyList(); + if (ipv4) return singletonList(wifi.getFirst()); + InetAddress slaac = getSlaacAddressForInterface(wifi.getFirst()); + return slaac == null ? emptyList() : singletonList(slaac); } - private List getUsableLocalIpv4Addresses() { - // If the device doesn't have wifi, don't open any sockets - if (wifiManager == null) return emptyList(); + /** + * Returns a {@link Pair} where the first element is the IPv4 address of + * the wifi interface and the second element is true if this device is + * providing an access point, or false if this device is a client. Returns + * null if this device isn't connected to wifi as an access point or client. + */ + @Nullable + private Pair getWifiIpv4Address() { + if (wifiManager == null) return null; // If we're connected to a wifi network, return its address WifiInfo info = wifiManager.getConnectionInfo(); if (info != null && info.getIpAddress() != 0) { - return singletonList(intToInetAddress(info.getIpAddress())); + return new Pair<>(intToInetAddress(info.getIpAddress()), false); } - // If we're running an access point, return its address - for (InetAddress addr : getLocalInetAddresses()) { - if (addr.equals(WIFI_AP_ADDRESS)) return singletonList(addr); - if (addr.equals(WIFI_DIRECT_AP_ADDRESS)) return singletonList(addr); + List addrs = getLocalInetAddresses(); + // If we're providing a normal access point, return its address + for (InetAddress addr : addrs) { + if (WIFI_AP_ADDRESS.equals(addr)) { + return new Pair<>(addr, true); + } } - // No suitable addresses - return emptyList(); + // If we're providing a wifi direct access point, return its address + for (InetAddress addr : addrs) { + if (WIFI_DIRECT_AP_ADDRESS.equals(addr)) { + return new Pair<>(addr, true); + } + } + // Not connected to wifi + return null; } - private List getUsableLocalIpv6Addresses() { - // If the device doesn't have wifi, don't open any sockets - if (wifiManager == null) return emptyList(); - // If we have a SLAAC address, return it - for (InetAddress addr : getLocalInetAddresses()) { - if (isSlaacAddress(addr)) return singletonList(addr); + @Nullable + private InetAddress getSlaacAddressForInterface(InetAddress wifi) { + try { + NetworkInterface iface = NetworkInterface.getByInetAddress(wifi); + if (iface == null) return null; + for (InetAddress addr : list(iface.getInetAddresses())) { + if (isSlaacAddress(addr)) return addr; + } + // No suitable address + return null; + } catch (SocketException e) { + logException(LOG, WARNING, e); + return null; } - // No suitable addresses - return emptyList(); } private InetAddress intToInetAddress(int ip) { @@ -153,9 +180,20 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { connectionStatusExecutor.execute(() -> { State s = getState(); if (s != ACTIVE && s != INACTIVE) return; - List addrs = getLocalInetAddresses(); - if (addrs.contains(WIFI_AP_ADDRESS) - || addrs.contains(WIFI_DIRECT_AP_ADDRESS)) { + Pair wifi = getWifiIpv4Address(); + if (wifi == null) { + LOG.info("Not connected to wifi"); + socketFactory = SocketFactory.getDefault(); + // Server sockets may not have been closed automatically when + // interface was taken down. If any sockets are open, closing + // them here will cause the sockets to be cleared and the state + // to be updated in acceptContactConnections() + if (s == ACTIVE) { + LOG.info("Closing server sockets"); + tryToClose(state.getServerSocket(true), LOG, WARNING); + tryToClose(state.getServerSocket(false), LOG, WARNING); + } + } else if (wifi.getSecond()) { LOG.info("Providing wifi hotspot"); // There's no corresponding Network object and thus no way // to get a suitable socket factory, so we won't be able to @@ -163,17 +201,6 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { // has internet access socketFactory = SocketFactory.getDefault(); if (s == INACTIVE) bind(); - } else if (addrs.isEmpty()) { - LOG.info("Not connected to wifi"); - socketFactory = SocketFactory.getDefault(); - // Server sockets may not have been closed automatically when - // interface was taken down. Sockets will be cleared and state - // updated in acceptContactConnections() - if (s == ACTIVE) { - LOG.info("Closing server sockets"); - tryToClose(state.getServerSocket(true), LOG, WARNING); - tryToClose(state.getServerSocket(false), LOG, WARNING); - } } else { LOG.info("Connected to wifi"); socketFactory = getSocketFactory(); From e065d45d1675e3b36d23acb642835828ba5eb0cb Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 9 Mar 2020 13:23:22 +0000 Subject: [PATCH 57/69] Recognise unusual AP addresses. --- .../plugin/tcp/AndroidLanTcpPlugin.java | 45 ++++++++++++++++--- .../bramble/plugin/tcp/LanTcpPlugin.java | 7 ++- 2 files changed, 43 insertions(+), 9 deletions(-) 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 bb5bd6e26..bddc94cc4 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 @@ -18,6 +18,7 @@ import org.briarproject.bramble.api.settings.Settings; import java.io.IOException; import java.net.InetAddress; +import java.net.InterfaceAddress; import java.net.NetworkInterface; import java.net.Socket; import java.net.SocketException; @@ -110,23 +111,53 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { if (info != null && info.getIpAddress() != 0) { return new Pair<>(intToInetAddress(info.getIpAddress()), false); } - List addrs = getLocalInetAddresses(); + List ifAddrs = getLocalInterfaceAddresses(); // If we're providing a normal access point, return its address - for (InetAddress addr : addrs) { - if (WIFI_AP_ADDRESS.equals(addr)) { - return new Pair<>(addr, true); + for (InterfaceAddress ifAddr : ifAddrs) { + if (isAndroidWifiApAddress(ifAddr)) { + return new Pair<>(ifAddr.getAddress(), true); } } // If we're providing a wifi direct access point, return its address - for (InetAddress addr : addrs) { - if (WIFI_DIRECT_AP_ADDRESS.equals(addr)) { - return new Pair<>(addr, true); + for (InterfaceAddress ifAddr : ifAddrs) { + if (isAndroidWifiDirectApAddress(ifAddr)) { + return new Pair<>(ifAddr.getAddress(), true); } } // Not connected to wifi return null; } + /** + * Returns true if the given address belongs to a network provided by an + * Android access point (including the access point's own address). + *

+ * The access point's address is usually 192.168.43.1, but at least one + * device (Honor 8A) may use other addresses in the range 192.168.43.0/24. + */ + private boolean isAndroidWifiApAddress(InterfaceAddress ifAddr) { + if (ifAddr.getNetworkPrefixLength() != 24) return false; + byte[] ip = ifAddr.getAddress().getAddress(); + return ip.length == 4 + && ip[0] == (byte) 192 + && ip[1] == (byte) 168 + && ip[2] == (byte) 43; + } + + /** + * Returns true if the given address belongs to a network provided by an + * Android wifi direct legacy mode access point (including the access + * point's own address). + */ + private boolean isAndroidWifiDirectApAddress(InterfaceAddress ifAddr) { + if (ifAddr.getNetworkPrefixLength() != 24) return false; + byte[] ip = ifAddr.getAddress().getAddress(); + return ip.length == 4 + && ip[0] == (byte) 192 + && ip[1] == (byte) 168 + && ip[2] == (byte) 49; + } + @Nullable private InetAddress getSlaacAddressForInterface(InetAddress wifi) { try { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java index b3c583728..27ceddbaa 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java @@ -67,14 +67,17 @@ class LanTcpPlugin extends TcpPlugin { /** * The IP address of an Android device providing a wifi access point. + *

+ * Most devices use this address, but at least one device (Honor 8A) may + * use other addresses in the range 192.168.43.0/24. */ - protected static final InetAddress WIFI_AP_ADDRESS; + private static final InetAddress WIFI_AP_ADDRESS; /** * The IP address of an Android device providing a wifi direct * legacy mode access point. */ - protected static final InetAddress WIFI_DIRECT_AP_ADDRESS; + private static final InetAddress WIFI_DIRECT_AP_ADDRESS; static { try { From badc6da6499aeaffe911eafa9bca98b3730c1878 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Tue, 10 Mar 2020 14:04:26 +0000 Subject: [PATCH 58/69] Accept any link-local IPv6 address. This allows IPv6 to be used when providing an access point. --- .../plugin/tcp/AndroidLanTcpPlugin.java | 8 +- .../bramble/api/plugin/LanTcpConstants.java | 5 +- .../bramble/plugin/tcp/LanTcpPlugin.java | 117 +++++++++--------- 3 files changed, 63 insertions(+), 67 deletions(-) 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 bddc94cc4..ec6c83e62 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 @@ -93,8 +93,8 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { Pair wifi = getWifiIpv4Address(); if (wifi == null) return emptyList(); if (ipv4) return singletonList(wifi.getFirst()); - InetAddress slaac = getSlaacAddressForInterface(wifi.getFirst()); - return slaac == null ? emptyList() : singletonList(slaac); + InetAddress ipv6 = getIpv6AddressForInterface(wifi.getFirst()); + return ipv6 == null ? emptyList() : singletonList(ipv6); } /** @@ -159,12 +159,12 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { } @Nullable - private InetAddress getSlaacAddressForInterface(InetAddress wifi) { + private InetAddress getIpv6AddressForInterface(InetAddress wifi) { try { NetworkInterface iface = NetworkInterface.getByInetAddress(wifi); if (iface == null) return null; for (InetAddress addr : list(iface.getInetAddresses())) { - if (isSlaacAddress(addr)) return addr; + if (isIpv6LinkLocalAddress(addr)) return addr; } // No suitable address return null; diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/LanTcpConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/LanTcpConstants.java index 835d67b5a..69880b4ab 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/LanTcpConstants.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/LanTcpConstants.java @@ -7,8 +7,9 @@ public interface LanTcpConstants { // Transport properties (shared with contacts) String PROP_IP_PORTS = "ipPorts"; String PROP_PORT = "port"; - String PROP_SLAAC = "slaac"; + String PROP_IPV6 = "ipv6"; - // A local setting + // Local settings (not shared with contacts) String PREF_LAN_IP_PORTS = "ipPorts"; + String PREF_IPV6 = "ipv6"; } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java index 27ceddbaa..98b077b3f 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java @@ -22,6 +22,8 @@ import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.Deque; +import java.util.LinkedList; import java.util.List; import java.util.concurrent.Executor; import java.util.logging.Logger; @@ -31,17 +33,18 @@ import javax.annotation.Nullable; import static java.lang.Integer.parseInt; import static java.util.Collections.addAll; import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; import static java.util.Collections.sort; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN; import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID; +import static org.briarproject.bramble.api.plugin.LanTcpConstants.PREF_IPV6; import static org.briarproject.bramble.api.plugin.LanTcpConstants.PREF_LAN_IP_PORTS; +import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_IPV6; import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_IP_PORTS; import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_PORT; -import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_SLAAC; +import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH; import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED; import static org.briarproject.bramble.util.IoUtils.tryToClose; import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress; @@ -49,22 +52,15 @@ import static org.briarproject.bramble.util.StringUtils.fromHexString; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; import static org.briarproject.bramble.util.StringUtils.join; import static org.briarproject.bramble.util.StringUtils.toHexString; +import static org.briarproject.bramble.util.StringUtils.utf8IsTooLong; @NotNullByDefault class LanTcpPlugin extends TcpPlugin { private static final Logger LOG = getLogger(LanTcpPlugin.class.getName()); - private static final int MAX_ADDRESSES = 4; private static final String SEPARATOR = ","; - /** - * The network prefix of a SLAAC IPv6 address. See - * https://tools.ietf.org/html/rfc4862#section-5.3 - */ - private static final byte[] SLAAC_PREFIX = - new byte[] {(byte) 0xFE, (byte) 0x80, 0, 0, 0, 0, 0, 0}; - /** * The IP address of an Android device providing a wifi access point. *

@@ -187,56 +183,48 @@ class LanTcpPlugin extends TcpPlugin { private void setLocalIpv4SocketAddress(InetSocketAddress a) { String ipPort = getIpPortString(a); + updateRecentAddresses(PREF_LAN_IP_PORTS, PROP_IP_PORTS, ipPort); + } + + private void setLocalIpv6SocketAddress(InetSocketAddress a) { + String hex = toHexString(a.getAddress().getAddress()); + updateRecentAddresses(PREF_IPV6, PROP_IPV6, hex); + } + + private void updateRecentAddresses(String settingKey, String propertyKey, + String item) { // Get the list of recently used addresses - String setting = callback.getSettings().get(PREF_LAN_IP_PORTS); - List recent = new ArrayList<>(); - if (!isNullOrEmpty(setting)) + String setting = callback.getSettings().get(settingKey); + Deque recent = new LinkedList<>(); + if (!isNullOrEmpty(setting)) { addAll(recent, setting.split(SEPARATOR)); - // Is the address already in the list? - if (recent.remove(ipPort)) { - // Move the address to the start of the list - recent.add(0, ipPort); + } + if (recent.remove(item)) { + // Move the item to the start of the list + recent.addFirst(item); setting = join(recent, SEPARATOR); } else { - // Add the address to the start of the list - recent.add(0, ipPort); - // Drop the least recently used address if the list is full - if (recent.size() > MAX_ADDRESSES) - recent = recent.subList(0, MAX_ADDRESSES); + // Add the item to the start of the list + recent.addFirst(item); + // Drop items from the end of the list if it's too long to encode setting = join(recent, SEPARATOR); + while (utf8IsTooLong(setting, MAX_PROPERTY_LENGTH)) { + recent.removeLast(); + setting = join(recent, SEPARATOR); + } // Update the list of addresses shared with contacts - List shared = new ArrayList<>(recent); - sort(shared); - String property = join(shared, SEPARATOR); TransportProperties properties = new TransportProperties(); - properties.put(PROP_IP_PORTS, property); + properties.put(propertyKey, setting); callback.mergeLocalProperties(properties); } // Save the setting Settings settings = new Settings(); - settings.put(PREF_LAN_IP_PORTS, setting); + settings.put(settingKey, setting); callback.mergeSettings(settings); } - private void setLocalIpv6SocketAddress(InetSocketAddress a) { - if (isSlaacAddress(a.getAddress())) { - String property = toHexString(a.getAddress().getAddress()); - TransportProperties properties = new TransportProperties(); - properties.put(PROP_SLAAC, property); - callback.mergeLocalProperties(properties); - } - } - - // See https://tools.ietf.org/html/rfc4862#section-5.3 - protected boolean isSlaacAddress(InetAddress a) { - if (!(a instanceof Inet6Address)) return false; - byte[] ip = a.getAddress(); - for (int i = 0; i < 8; i++) { - if (ip[i] != SLAAC_PREFIX[i]) return false; - } - return (ip[8] & 0x02) == 0x02 - && ip[11] == (byte) 0xFF - && ip[12] == (byte) 0xFE; + protected boolean isIpv6LinkLocalAddress(InetAddress a) { + return a instanceof Inet6Address && a.isLinkLocalAddress(); } @Override @@ -266,22 +254,30 @@ class LanTcpPlugin extends TcpPlugin { private List getRemoteIpv6SocketAddresses( TransportProperties p) { - InetAddress slaac = parseSlaacProperty(p.get(PROP_SLAAC)); + List addrs = parseIpv6Addresses(p.get(PROP_IPV6)); int port = parsePortProperty(p.get(PROP_PORT)); - if (slaac == null || port == 0) return emptyList(); - return singletonList(new InetSocketAddress(slaac, port)); + if (addrs.isEmpty() || port == 0) return emptyList(); + List remotes = new ArrayList<>(); + for (InetAddress addr : addrs) { + remotes.add(new InetSocketAddress(addr, port)); + } + return remotes; } - @Nullable - private InetAddress parseSlaacProperty(String slaacProperty) { - if (isNullOrEmpty(slaacProperty)) return null; + private List parseIpv6Addresses(String property) { + if (isNullOrEmpty(property)) return emptyList(); try { - byte[] ip = fromHexString(slaacProperty); - if (ip.length != 16) return null; - InetAddress a = InetAddress.getByAddress(ip); - return isSlaacAddress(a) ? a : null; + List addrs = new ArrayList<>(); + for (String hex : property.split(SEPARATOR)) { + byte[] ip = fromHexString(hex); + if (ip.length == 16) { + InetAddress addr = InetAddress.getByAddress(ip); + if (isIpv6LinkLocalAddress(addr)) addrs.add(addr); + } + } + return addrs; } catch (IllegalArgumentException | UnknownHostException e) { - return null; + return emptyList(); } } @@ -289,13 +285,12 @@ class LanTcpPlugin extends TcpPlugin { if (ipv4) { // Accept link-local and site-local IPv4 addresses boolean isIpv4 = a instanceof Inet4Address; - boolean loop = a.isLoopbackAddress(); boolean link = a.isLinkLocalAddress(); boolean site = a.isSiteLocalAddress(); - return isIpv4 && !loop && (link || site); + return isIpv4 && (link || site); } else { - // Accept IPv6 SLAAC addresses - return isSlaacAddress(a); + // Accept link-local IPv6 addresses + return isIpv6LinkLocalAddress(a); } } From 1495daf977670fb18a36ff4f15d258e4dd5ca07a Mon Sep 17 00:00:00 2001 From: akwizgran Date: Tue, 10 Mar 2020 14:48:28 +0000 Subject: [PATCH 59/69] Remove redundant address checks. Remote addresses are checked for acceptability before connecting. --- .../org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java index 98b077b3f..3126cfc45 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java @@ -270,10 +270,7 @@ class LanTcpPlugin extends TcpPlugin { List addrs = new ArrayList<>(); for (String hex : property.split(SEPARATOR)) { byte[] ip = fromHexString(hex); - if (ip.length == 16) { - InetAddress addr = InetAddress.getByAddress(ip); - if (isIpv6LinkLocalAddress(addr)) addrs.add(addr); - } + if (ip.length == 16) addrs.add(InetAddress.getByAddress(ip)); } return addrs; } catch (IllegalArgumentException | UnknownHostException e) { From 25e50ceb1063fef66f988c8b52383eaa5743cea4 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Wed, 11 Mar 2020 15:59:23 +0000 Subject: [PATCH 60/69] Try to detect our address on an IPv6-only wifi network. --- .../plugin/tcp/AndroidLanTcpPlugin.java | 61 ++++++++++++++++--- 1 file changed, 51 insertions(+), 10 deletions(-) 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 ec6c83e62..3510e571b 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,9 +1,12 @@ package org.briarproject.bramble.plugin.tcp; +import android.annotation.TargetApi; import android.content.Context; import android.net.ConnectivityManager; +import android.net.LinkAddress; +import android.net.LinkProperties; import android.net.Network; -import android.net.NetworkInfo; +import android.net.NetworkCapabilities; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; @@ -32,7 +35,7 @@ import javax.net.SocketFactory; import static android.content.Context.CONNECTIVITY_SERVICE; import static android.content.Context.WIFI_SERVICE; -import static android.net.ConnectivityManager.TYPE_WIFI; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.os.Build.VERSION.SDK_INT; import static java.util.Collections.emptyList; import static java.util.Collections.list; @@ -90,11 +93,22 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { @Override protected List getUsableLocalInetAddresses(boolean ipv4) { + InetAddress addr = getWifiAddress(ipv4); + return addr == null ? emptyList() : singletonList(addr); + } + + @Nullable + private InetAddress getWifiAddress(boolean ipv4) { Pair wifi = getWifiIpv4Address(); - if (wifi == null) return emptyList(); - if (ipv4) return singletonList(wifi.getFirst()); - InetAddress ipv6 = getIpv6AddressForInterface(wifi.getFirst()); - return ipv6 == null ? emptyList() : singletonList(ipv6); + if (ipv4) return wifi == null ? null : wifi.getFirst(); + // If there's no wifi IPv4 address, we might be a client on an + // IPv6-only wifi network. We can only detect this on API 21+ + if (wifi == null) { + return SDK_INT >= 21 ? getWifiClientIpv6Address() : null; + } + // Use the wifi IPv4 address to determine which interface's IPv6 + // address we should return (if the interface has a suitable address) + return getIpv6AddressForInterface(wifi.getFirst()); } /** @@ -158,10 +172,35 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { && ip[2] == (byte) 49; } + /** + * Returns a link-local IPv6 address for the wifi client interface, or null + * if there's no such interface or it doesn't have a suitable address. + */ + @TargetApi(21) @Nullable - private InetAddress getIpv6AddressForInterface(InetAddress wifi) { + private InetAddress getWifiClientIpv6Address() { + for (Network net : connectivityManager.getAllNetworks()) { + NetworkCapabilities caps = + connectivityManager.getNetworkCapabilities(net); + if (caps == null || !caps.hasTransport(TRANSPORT_WIFI)) continue; + LinkProperties props = connectivityManager.getLinkProperties(net); + if (props == null) continue; + for (LinkAddress linkAddress : props.getLinkAddresses()) { + InetAddress addr = linkAddress.getAddress(); + if (isIpv6LinkLocalAddress(addr)) return addr; + } + } + return null; + } + + /** + * Returns a link-local IPv6 address for the interface with the given IPv4 + * address, or null if the interface doesn't have a suitable address. + */ + @Nullable + private InetAddress getIpv6AddressForInterface(InetAddress ipv4) { try { - NetworkInterface iface = NetworkInterface.getByInetAddress(wifi); + NetworkInterface iface = NetworkInterface.getByInetAddress(ipv4); if (iface == null) return null; for (InetAddress addr : list(iface.getInetAddresses())) { if (isIpv6LinkLocalAddress(addr)) return addr; @@ -193,9 +232,11 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { private SocketFactory getSocketFactory() { if (SDK_INT < 21) return SocketFactory.getDefault(); for (Network net : connectivityManager.getAllNetworks()) { - NetworkInfo info = connectivityManager.getNetworkInfo(net); - if (info != null && info.getType() == TYPE_WIFI) + NetworkCapabilities caps = + connectivityManager.getNetworkCapabilities(net); + if (caps != null && caps.hasTransport(TRANSPORT_WIFI)) { return net.getSocketFactory(); + } } LOG.warning("Could not find suitable socket factory"); return SocketFactory.getDefault(); From 5567982fb4c1e36bfd0492c242e0e1a432312489 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Wed, 11 Mar 2020 16:15:46 +0000 Subject: [PATCH 61/69] Use IPv6 for key agreement if IPv4 isn't available. --- .../briarproject/bramble/plugin/tcp/LanTcpPlugin.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java index 3126cfc45..b7d64b3fa 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java @@ -329,7 +329,7 @@ class LanTcpPlugin extends TcpPlugin { @Override public KeyAgreementListener createKeyAgreementListener(byte[] commitment) { ServerSocket ss = null; - for (InetSocketAddress addr : getLocalSocketAddresses(true)) { + for (InetSocketAddress addr : getLocalSocketAddresses()) { // Don't try to reuse the same port we use for contact connections addr = new InetSocketAddress(addr.getAddress(), 0); try { @@ -355,6 +355,13 @@ class LanTcpPlugin extends TcpPlugin { return new LanKeyAgreementListener(descriptor, ss); } + private List getLocalSocketAddresses() { + List addrs = new ArrayList<>(); + addrs.addAll(getLocalSocketAddresses(true)); + addrs.addAll(getLocalSocketAddresses(false)); + return addrs; + } + @Override public DuplexTransportConnection createKeyAgreementConnection( byte[] commitment, BdfList descriptor) { From 6fb4b95b1802f54a9691dd7593e04a7a5787f416 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Wed, 11 Mar 2020 17:19:09 +0000 Subject: [PATCH 62/69] Don't allow list elements to be null. --- .../bramble/plugin/tcp/TcpPlugin.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java index 954868367..522d1f0d2 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java @@ -43,7 +43,6 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.ThreadSafe; -import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.list; import static java.util.logging.Level.INFO; @@ -206,9 +205,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { @Override public void stop() { - for (@Nullable ServerSocket ss : state.setStopped()) { - tryToClose(ss, LOG, WARNING); - } + for (ServerSocket ss : state.setStopped()) tryToClose(ss, LOG, WARNING); } @Override @@ -410,9 +407,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { State s = getState(); if (!toClose.isEmpty()) { LOG.info("Disabled by user, closing server sockets"); - for (@Nullable ServerSocket ss : toClose) { - tryToClose(ss, LOG, WARNING); - } + for (ServerSocket ss : toClose) tryToClose(ss, LOG, WARNING); } else if (s == INACTIVE) { LOG.info("Enabled by user, opening server sockets"); bind(); @@ -445,9 +440,15 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { @GuardedBy("this") private List clearServerSockets() { - List toClose = asList(serverSocketV4, serverSocketV6); - serverSocketV4 = null; - serverSocketV6 = null; + List toClose = new ArrayList<>(2); + if (serverSocketV4 != null) { + toClose.add(serverSocketV4); + serverSocketV4 = null; + } + if (serverSocketV6 != null) { + toClose.add(serverSocketV6); + serverSocketV6 = null; + } return toClose; } From 0a666df164f2ef04a15acd6378ab3ef64dde3246 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 12 Mar 2020 16:43:46 +0000 Subject: [PATCH 63/69] Correctly handle connectivity events on IPv6-only networks. --- .../plugin/tcp/AndroidLanTcpPlugin.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) 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 3510e571b..cabb2f653 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 @@ -252,7 +252,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { connectionStatusExecutor.execute(() -> { State s = getState(); if (s != ACTIVE && s != INACTIVE) return; - Pair wifi = getWifiIpv4Address(); + Pair wifi = getPreferredWifiAddress(); if (wifi == null) { LOG.info("Not connected to wifi"); socketFactory = SocketFactory.getDefault(); @@ -280,4 +280,23 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { } }); } + + /** + * Returns a {@link Pair} where the first element is an IP address (IPv4 if + * available, otherwise IPv6) of the wifi interface and the second element + * is true if this device is providing an access point, or false if this + * device is a client. Returns null if this device isn't connected to wifi + * as an access point or client. + */ + @Nullable + private Pair getPreferredWifiAddress() { + Pair wifi = getWifiIpv4Address(); + // If there's no wifi IPv4 address, we might be a client on an + // IPv6-only wifi network. We can only detect this on API 21+ + if (wifi == null && SDK_INT >= 21) { + InetAddress ipv6 = getWifiClientIpv6Address(); + if (ipv6 != null) return new Pair<>(ipv6, false); + } + return wifi; + } } \ No newline at end of file From e4940a046ae36d3f9d5175b6d1f71812887f742e Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 24 Apr 2020 15:22:49 +0100 Subject: [PATCH 64/69] Move transport toggles back to the settings screen. --- .../android/navdrawer/NavDrawerActivity.java | 138 ++++++++++- .../android/navdrawer/NavDrawerModule.java | 4 + .../android/navdrawer/NavDrawerViewModel.java | 117 +--------- .../navdrawer/PluginViewController.java | 200 ---------------- .../android/navdrawer/PluginViewModel.java | 215 ++++++++++++++++++ .../android/settings/SettingsFragment.java | 76 ++++++- .../res/drawable/ic_baseline_drag_handle.xml | 12 - .../src/main/res/layout/navigation_menu.xml | 53 ++++- .../res/layout/navigation_menu_collapsed.xml | 165 -------------- .../res/layout/navigation_menu_expanded.xml | 183 --------------- .../src/main/res/layout/transports_list.xml | 19 ++ briar-android/src/main/res/values/strings.xml | 25 +- briar-android/src/main/res/xml/settings.xml | 28 +++ 13 files changed, 530 insertions(+), 705 deletions(-) delete mode 100644 briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java create mode 100644 briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewModel.java delete mode 100644 briar-android/src/main/res/drawable/ic_baseline_drag_handle.xml delete mode 100644 briar-android/src/main/res/layout/navigation_menu_collapsed.xml delete mode 100644 briar-android/src/main/res/layout/navigation_menu_expanded.xml create mode 100644 briar-android/src/main/res/layout/transports_list.xml diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java index 0ea593c9e..1ea8f9772 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java @@ -4,9 +4,12 @@ import android.content.Intent; import android.content.res.Configuration; import android.net.Uri; import android.os.Bundle; +import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.GridView; import android.widget.ImageView; import android.widget.TextView; @@ -17,6 +20,11 @@ import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; +import org.briarproject.bramble.api.plugin.BluetoothConstants; +import org.briarproject.bramble.api.plugin.LanTcpConstants; +import org.briarproject.bramble.api.plugin.Plugin.State; +import org.briarproject.bramble.api.plugin.TorConstants; +import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BriarActivity; @@ -29,16 +37,21 @@ import org.briarproject.briar.android.logout.SignOutFragment; import org.briarproject.briar.android.privategroup.list.GroupListFragment; import org.briarproject.briar.android.settings.SettingsActivity; +import java.util.ArrayList; +import java.util.List; import java.util.logging.Logger; import javax.inject.Inject; +import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.StringRes; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.widget.Toolbar; import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; @@ -53,6 +66,9 @@ import static androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE; import static java.util.Objects.requireNonNull; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING; +import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; +import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING; +import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; import static org.briarproject.briar.android.BriarService.EXTRA_STARTUP_FAILED; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD; import static org.briarproject.briar.android.navdrawer.IntentRouter.handleExternalIntent; @@ -79,7 +95,8 @@ public class NavDrawerActivity extends BriarActivity implements public static Uri SIGN_OUT_URI = Uri.parse("briar-content://org.briarproject.briar/sign-out"); - private NavDrawerViewModel viewModel; + private NavDrawerViewModel navDrawerViewModel; + private PluginViewModel pluginViewModel; private ActionBarDrawerToggle drawerToggle; @Inject @@ -91,6 +108,9 @@ public class NavDrawerActivity extends BriarActivity implements private DrawerLayout drawerLayout; private NavigationView navigation; + private List transports; + private BaseAdapter transportsAdapter; + @Override public void injectActivity(ActivityComponent component) { component.inject(this); @@ -102,20 +122,21 @@ public class NavDrawerActivity extends BriarActivity implements exitIfStartupFailed(getIntent()); setContentView(R.layout.activity_nav_drawer); - viewModel = ViewModelProviders.of(this, viewModelFactory) - .get(NavDrawerViewModel.class); + ViewModelProvider provider = + ViewModelProviders.of(this, viewModelFactory); + navDrawerViewModel = provider.get(NavDrawerViewModel.class); + pluginViewModel = provider.get(PluginViewModel.class); - viewModel.showExpiryWarning().observe(this, this::showExpiryWarning); - viewModel.shouldAskForDozeWhitelisting().observe(this, ask -> { + navDrawerViewModel.showExpiryWarning() + .observe(this, this::showExpiryWarning); + navDrawerViewModel.shouldAskForDozeWhitelisting().observe(this, ask -> { if (ask) showDozeDialog(getString(R.string.setup_doze_intro)); }); - View drawerScrollView = findViewById(R.id.drawerScrollView); - new PluginViewController(drawerScrollView, this, viewModel); - Toolbar toolbar = findViewById(R.id.toolbar); drawerLayout = findViewById(R.id.drawer_layout); navigation = findViewById(R.id.navigation); + GridView transportsView = findViewById(R.id.transportsView); setSupportActionBar(toolbar); ActionBar actionBar = requireNonNull(getSupportActionBar()); @@ -128,6 +149,9 @@ public class NavDrawerActivity extends BriarActivity implements drawerLayout.addDrawerListener(drawerToggle); navigation.setNavigationItemSelectedListener(this); + initializeTransports(); + transportsView.setAdapter(transportsAdapter); + lockManager.isLockable().observe(this, this::setLockVisible); if (lifecycleManager.getLifecycleState().isAfter(RUNNING)) { @@ -146,7 +170,7 @@ public class NavDrawerActivity extends BriarActivity implements public void onStart() { super.onStart(); lockManager.checkIfLockable(); - viewModel.checkExpiryWarning(); + navDrawerViewModel.checkExpiryWarning(); } @Override @@ -154,7 +178,7 @@ public class NavDrawerActivity extends BriarActivity implements @Nullable Intent data) { super.onActivityResult(request, result, data); if (request == REQUEST_PASSWORD && result == RESULT_OK) { - viewModel.checkDozeWhitelisting(); + navDrawerViewModel.checkDozeWhitelisting(); } } @@ -343,11 +367,101 @@ public class NavDrawerActivity extends BriarActivity implements ImageView expiryWarningClose = expiryWarning.findViewById(R.id.expiryWarningClose); expiryWarningClose.setOnClickListener(v -> - viewModel.expiryWarningDismissed() - ); + navDrawerViewModel.expiryWarningDismissed()); expiryWarning.setVisibility(VISIBLE); } else { expiryWarning.setVisibility(GONE); } } + + private void initializeTransports() { + transports = new ArrayList<>(3); + + transportsAdapter = new BaseAdapter() { + + @Override + public int getCount() { + return transports.size(); + } + + @Override + public Transport getItem(int position) { + return transports.get(position); + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override + public View getView(int position, View convertView, + ViewGroup parent) { + View view; + if (convertView != null) { + view = convertView; + } else { + LayoutInflater inflater = getLayoutInflater(); + view = inflater.inflate(R.layout.list_item_transport, + parent, false); + } + + Transport t = getItem(position); + + ImageView icon = view.findViewById(R.id.imageView); + icon.setImageDrawable(ContextCompat + .getDrawable(NavDrawerActivity.this, t.iconId)); + icon.setColorFilter(getIconColour(t.state)); + + TextView text = view.findViewById(R.id.textView); + text.setText(getString(t.textId)); + + return view; + } + }; + + transports.add(createTransport(TorConstants.ID, + R.drawable.transport_tor, R.string.transport_tor)); + transports.add(createTransport(LanTcpConstants.ID, + R.drawable.transport_lan, R.string.transport_lan)); + transports.add(createTransport(BluetoothConstants.ID, + R.drawable.transport_bt, R.string.transport_bt)); + } + + private int getIconColour(State state) { + int colorRes; + if (state == ACTIVE) { + colorRes = R.color.briar_green_light; + } else if (state == ENABLING) { + colorRes = R.color.briar_yellow; + } else { + colorRes = android.R.color.tertiary_text_light; + } + return ContextCompat.getColor(this, colorRes); + } + + private Transport createTransport(TransportId id, @DrawableRes int iconId, + @StringRes int textId) { + Transport transport = new Transport(iconId, textId); + pluginViewModel.getPluginState(id).observe(this, state -> { + transport.state = state; + transportsAdapter.notifyDataSetChanged(); + }); + return transport; + } + + private static class Transport { + + @DrawableRes + private final int iconId; + @StringRes + private final int textId; + + private State state = STARTING_STOPPING; + + private Transport(@DrawableRes int iconId, @StringRes int textId) { + this.iconId = iconId; + this.textId = textId; + } + } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerModule.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerModule.java index 966c178a2..e7282b1c7 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerModule.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerModule.java @@ -16,4 +16,8 @@ public abstract class NavDrawerModule { abstract ViewModel bindNavDrawerViewModel( NavDrawerViewModel navDrawerViewModel); + @Binds + @IntoMap + @ViewModelKey(PluginViewModel.class) + abstract ViewModel bindPluginViewModel(PluginViewModel pluginViewModel); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java index 394f1b7c3..76c1aeb83 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java @@ -4,54 +4,36 @@ import android.app.Application; import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DbException; -import org.briarproject.bramble.api.event.Event; -import org.briarproject.bramble.api.event.EventBus; -import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.BluetoothConstants; import org.briarproject.bramble.api.plugin.LanTcpConstants; -import org.briarproject.bramble.api.plugin.Plugin; -import org.briarproject.bramble.api.plugin.Plugin.State; -import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.TorConstants; import org.briarproject.bramble.api.plugin.TransportId; -import org.briarproject.bramble.api.plugin.event.TransportStateEvent; import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.SettingsManager; -import org.briarproject.bramble.api.system.LocationUtils; import java.util.concurrent.Executor; import java.util.logging.Logger; import javax.inject.Inject; -import androidx.annotation.Nullable; import androidx.annotation.UiThread; import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import static java.util.concurrent.TimeUnit.DAYS; -import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; -import static org.briarproject.bramble.api.plugin.Plugin.PREF_PLUGIN_ENABLE; -import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; -import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE; -import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK; -import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_WITH_BRIDGES; -import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ONLY_WHEN_CHARGING; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE; import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD; import static org.briarproject.briar.android.controller.BriarControllerImpl.DOZE_ASK_AGAIN; import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE; -import static org.briarproject.briar.android.util.UiUtils.getCountryDisplayName; import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting; @NotNullByDefault -public class NavDrawerViewModel extends AndroidViewModel - implements EventListener { +public class NavDrawerViewModel extends AndroidViewModel { private static final Logger LOG = getLogger(NavDrawerViewModel.class.getName()); @@ -63,53 +45,18 @@ public class NavDrawerViewModel extends AndroidViewModel @DatabaseExecutor private final Executor dbExecutor; private final SettingsManager settingsManager; - private final PluginManager pluginManager; - private final LocationUtils locationUtils; - private final EventBus eventBus; private final MutableLiveData showExpiryWarning = new MutableLiveData<>(); private final MutableLiveData shouldAskForDozeWhitelisting = new MutableLiveData<>(); - private final MutableLiveData torPluginState = - new MutableLiveData<>(); - private final MutableLiveData wifiPluginState = - new MutableLiveData<>(); - private final MutableLiveData btPluginState = - new MutableLiveData<>(); - @Inject NavDrawerViewModel(Application app, @DatabaseExecutor Executor dbExecutor, - SettingsManager settingsManager, PluginManager pluginManager, - LocationUtils locationUtils, EventBus eventBus) { + SettingsManager settingsManager) { super(app); this.dbExecutor = dbExecutor; this.settingsManager = settingsManager; - this.pluginManager = pluginManager; - this.locationUtils = locationUtils; - this.eventBus = eventBus; - eventBus.addListener(this); - updatePluginStates(); - } - - @Override - protected void onCleared() { - eventBus.removeListener(this); - } - - @Override - public void eventOccurred(Event e) { - if (e instanceof TransportStateEvent) { - TransportStateEvent t = (TransportStateEvent) e; - TransportId id = t.getTransportId(); - State state = t.getState(); - if (LOG.isLoggable(INFO)) { - LOG.info("TransportStateEvent: " + id + " is " + state); - } - MutableLiveData liveData = getPluginLiveData(id); - if (liveData != null) liveData.postValue(state); - } } LiveData showExpiryWarning() { @@ -192,64 +139,4 @@ public class NavDrawerViewModel extends AndroidViewModel } }); } - - private void updatePluginStates() { - for (TransportId t : TRANSPORT_IDS) { - MutableLiveData liveData = getPluginLiveData(t); - if (liveData == null) throw new AssertionError(); - liveData.setValue(getTransportState(t)); - } - } - - private State getTransportState(TransportId id) { - Plugin plugin = pluginManager.getPlugin(id); - return plugin == null ? STARTING_STOPPING : plugin.getState(); - } - - @Nullable - private MutableLiveData getPluginLiveData(TransportId t) { - if (t.equals(TorConstants.ID)) { - return torPluginState; - } else if (t.equals(LanTcpConstants.ID)) { - return wifiPluginState; - } else if (t.equals(BluetoothConstants.ID)) { - return btPluginState; - } else { - return null; - } - } - - LiveData getPluginState(TransportId t) { - LiveData liveData = getPluginLiveData(t); - if (liveData == null) throw new AssertionError(); - return liveData; - } - - int getReasonsDisabled(TransportId id) { - Plugin plugin = pluginManager.getPlugin(id); - return plugin == null ? 0 : plugin.getReasonsDisabled(); - } - - void setPluginEnabled(TransportId t, boolean enabled) { - pluginManager.setPluginEnabled(t, enabled); - } - - void setTorEnabled(boolean battery, boolean mobileData, boolean location) { - Settings s = new Settings(); - s.putBoolean(PREF_PLUGIN_ENABLE, true); - if (battery) s.putBoolean(PREF_TOR_ONLY_WHEN_CHARGING, false); - if (mobileData) s.putBoolean(PREF_TOR_MOBILE, true); - if (location) s.putInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_WITH_BRIDGES); - dbExecutor.execute(() -> { - try { - settingsManager.mergeSettings(s, TorConstants.ID.getString()); - } catch (DbException e) { - logException(LOG, WARNING, e); - } - }); - } - - String getCurrentCountryName() { - return getCountryDisplayName(locationUtils.getCurrentCountry()); - } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java deleted file mode 100644 index 3f551b1ee..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewController.java +++ /dev/null @@ -1,200 +0,0 @@ -package org.briarproject.briar.android.navdrawer; - -import android.view.View; -import android.widget.ImageView; -import android.widget.ScrollView; - -import org.briarproject.bramble.api.plugin.BluetoothConstants; -import org.briarproject.bramble.api.plugin.LanTcpConstants; -import org.briarproject.bramble.api.plugin.Plugin.State; -import org.briarproject.bramble.api.plugin.TorConstants; -import org.briarproject.bramble.api.plugin.TransportId; -import org.briarproject.briar.R; - -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.AppCompatImageButton; -import androidx.appcompat.widget.SwitchCompat; -import androidx.constraintlayout.widget.ConstraintLayout; -import androidx.constraintlayout.widget.ConstraintSet; - -import static android.os.Build.VERSION.SDK_INT; -import static android.transition.TransitionManager.beginDelayedTransition; -import static android.view.View.FOCUS_DOWN; -import static androidx.core.content.ContextCompat.getColor; -import static org.briarproject.bramble.api.plugin.Plugin.REASON_USER; -import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; -import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; -import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING; -import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; -import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY; -import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED; -import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA; -import static org.briarproject.briar.android.navdrawer.NavDrawerViewModel.TRANSPORT_IDS; -import static org.briarproject.briar.android.util.UiUtils.getDialogIcon; - -class PluginViewController { - - private final AppCompatActivity activity; - private final NavDrawerViewModel viewModel; - private final ConstraintLayout drawerContent; - private final ConstraintSet collapsedConstraints, expandedConstraints; - private final AppCompatImageButton chevronView; - private final ImageView torIcon, wifiIcon, btIcon; - private final SwitchCompat torSwitch, wifiSwitch, btSwitch; - - private boolean expanded = false; - - PluginViewController(View v, AppCompatActivity activity, - NavDrawerViewModel viewModel) { - this.activity = activity; - this.viewModel = viewModel; - drawerContent = v.findViewById(R.id.drawerContent); - - collapsedConstraints = new ConstraintSet(); - collapsedConstraints.clone(v.getContext(), - R.layout.navigation_menu_collapsed); - - expandedConstraints = new ConstraintSet(); - expandedConstraints.clone(v.getContext(), - R.layout.navigation_menu_expanded); - - // Scroll the drawer to the bottom when the view is expanded/collapsed - ScrollView scrollView = v.findViewById(R.id.drawerScrollView); - drawerContent.addOnLayoutChangeListener((view, left, top, right, - bottom, oldLeft, oldTop, oldRight, oldBottom) -> - scrollView.fullScroll(FOCUS_DOWN)); - - // Clicking the chevron expands or collapses the view - chevronView = v.findViewById(R.id.chevronView); - chevronView.setOnClickListener(view -> expandOrCollapseView()); - - // The whole view is clickable when collapsed - v.findViewById(R.id.connectionsBackground).setOnClickListener(view -> - expandOrCollapseView()); - - torIcon = v.findViewById(R.id.torIcon); - wifiIcon = v.findViewById(R.id.wifiIcon); - btIcon = v.findViewById(R.id.btIcon); - - torSwitch = v.findViewById(R.id.torSwitch); - wifiSwitch = v.findViewById(R.id.wifiSwitch); - btSwitch = v.findViewById(R.id.btSwitch); - - for (TransportId t : TRANSPORT_IDS) { - // a OnCheckedChangeListener would get triggered on programmatic updates - SwitchCompat switchCompat = getSwitch(t); - switchCompat.setOnClickListener(buttonView -> { - if (switchCompat.isChecked()) tryToEnablePlugin(t); - else viewModel.setPluginEnabled(t, false); - // Revert the switch to its previous state until the plugin - // changes its state - switchCompat.toggle(); - }); - viewModel.getPluginState(t).observe(activity, state -> - stateUpdate(t, state)); - } - } - - private void expandOrCollapseView() { - if (SDK_INT >= 19) beginDelayedTransition(drawerContent); - if (expanded) { - collapsedConstraints.applyTo(drawerContent); - chevronView.setImageResource(R.drawable.chevron_up_white); - } else { - expandedConstraints.applyTo(drawerContent); - chevronView.setImageResource(R.drawable.chevron_down_white); - } - expanded = !expanded; - } - - private void tryToEnablePlugin(TransportId id) { - if (id.equals(TorConstants.ID)) { - int reasons = viewModel.getReasonsDisabled(id); - if (reasons == 0 || reasons == REASON_USER) { - viewModel.setPluginEnabled(id, true); - } else { - showTorSettingsDialog(reasons); - } - } else { - viewModel.setPluginEnabled(id, true); - } - } - - private void stateUpdate(TransportId id, State state) { - updateIcon(getIcon(id), state); - updateSwitch(getSwitch(id), state); - } - - private SwitchCompat getSwitch(TransportId id) { - if (id == TorConstants.ID) return torSwitch; - if (id == BluetoothConstants.ID) return btSwitch; - if (id == LanTcpConstants.ID) return wifiSwitch; - throw new AssertionError(); - } - - private void updateSwitch(SwitchCompat switchCompat, State state) { - boolean checked = state != STARTING_STOPPING && state != DISABLED; - switchCompat.setChecked(checked); - switchCompat.setEnabled(state != STARTING_STOPPING); - } - - private ImageView getIcon(TransportId id) { - if (id == TorConstants.ID) return torIcon; - if (id == BluetoothConstants.ID) return btIcon; - if (id == LanTcpConstants.ID) return wifiIcon; - throw new AssertionError(); - } - - private void updateIcon(ImageView icon, State state) { - int colorRes; - if (state == ACTIVE) { - colorRes = R.color.briar_green_light; - } else if (state == ENABLING) { - colorRes = R.color.briar_yellow; - } else { - colorRes = android.R.color.tertiary_text_light; - } - int color = getColor(icon.getContext(), colorRes); - icon.setColorFilter(color); - } - - private void showTorSettingsDialog(int reasonsDisabled) { - boolean battery = (reasonsDisabled & REASON_BATTERY) != 0; - boolean mobileData = (reasonsDisabled & REASON_MOBILE_DATA) != 0; - boolean location = (reasonsDisabled & REASON_COUNTRY_BLOCKED) != 0; - - StringBuilder s = new StringBuilder(); - if (location) { - s.append("\t\u2022 "); - s.append(activity.getString(R.string.tor_override_network_setting, - viewModel.getCurrentCountryName())); - s.append('\n'); - } - if (mobileData) { - s.append("\t\u2022 "); - s.append(activity.getString( - R.string.tor_override_mobile_data_setting)); - s.append('\n'); - } - if (battery) { - s.append("\t\u2022 "); - s.append(activity.getString(R.string.tor_only_when_charging_title)); - s.append('\n'); - } - String message = activity.getString( - R.string.tor_override_settings_body, s.toString()); - - AlertDialog.Builder b = - new AlertDialog.Builder(activity, R.style.BriarDialogTheme); - b.setTitle(R.string.tor_override_settings_title); - b.setIcon(getDialogIcon(activity, R.drawable.ic_settings_black_24dp)); - b.setMessage(message); - b.setPositiveButton(R.string.tor_override_settings_confirm, - (dialog, which) -> - viewModel.setTorEnabled(battery, mobileData, location)); - b.setNegativeButton(R.string.cancel, (dialog, which) -> - dialog.dismiss()); - b.show(); - } -} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewModel.java new file mode 100644 index 000000000..f67736ba4 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewModel.java @@ -0,0 +1,215 @@ +package org.briarproject.briar.android.navdrawer; + +import android.app.Application; + +import org.briarproject.bramble.api.db.DatabaseExecutor; +import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.event.Event; +import org.briarproject.bramble.api.event.EventBus; +import org.briarproject.bramble.api.event.EventListener; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.BluetoothConstants; +import org.briarproject.bramble.api.plugin.LanTcpConstants; +import org.briarproject.bramble.api.plugin.Plugin; +import org.briarproject.bramble.api.plugin.Plugin.State; +import org.briarproject.bramble.api.plugin.PluginManager; +import org.briarproject.bramble.api.plugin.TorConstants; +import org.briarproject.bramble.api.plugin.TransportId; +import org.briarproject.bramble.api.plugin.event.TransportStateEvent; +import org.briarproject.bramble.api.settings.Settings; +import org.briarproject.bramble.api.settings.SettingsManager; +import org.briarproject.bramble.api.system.LocationUtils; +import org.briarproject.briar.R; + +import java.util.concurrent.Executor; +import java.util.logging.Logger; + +import javax.inject.Inject; + +import androidx.appcompat.app.AlertDialog; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; + +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.WARNING; +import static java.util.logging.Logger.getLogger; +import static org.briarproject.bramble.api.plugin.Plugin.PREF_PLUGIN_ENABLE; +import static org.briarproject.bramble.api.plugin.Plugin.REASON_USER; +import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; +import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE; +import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK; +import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_WITH_BRIDGES; +import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ONLY_WHEN_CHARGING; +import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY; +import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED; +import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA; +import static org.briarproject.bramble.util.LogUtils.logException; +import static org.briarproject.briar.android.navdrawer.NavDrawerViewModel.TRANSPORT_IDS; +import static org.briarproject.briar.android.util.UiUtils.getCountryDisplayName; +import static org.briarproject.briar.android.util.UiUtils.getDialogIcon; + +@NotNullByDefault +public class PluginViewModel extends AndroidViewModel implements EventListener { + + private static final Logger LOG = + getLogger(PluginViewModel.class.getName()); + + private final Application app; + @DatabaseExecutor + private final Executor dbExecutor; + private final SettingsManager settingsManager; + private final PluginManager pluginManager; + private final LocationUtils locationUtils; + private final EventBus eventBus; + + private final MutableLiveData torPluginState = + new MutableLiveData<>(); + private final MutableLiveData wifiPluginState = + new MutableLiveData<>(); + private final MutableLiveData btPluginState = + new MutableLiveData<>(); + + @Inject + PluginViewModel(Application app, @DatabaseExecutor Executor dbExecutor, + SettingsManager settingsManager, PluginManager pluginManager, + LocationUtils locationUtils, EventBus eventBus) { + super(app); + this.app = app; + this.dbExecutor = dbExecutor; + this.settingsManager = settingsManager; + this.pluginManager = pluginManager; + this.locationUtils = locationUtils; + this.eventBus = eventBus; + eventBus.addListener(this); + initialisePluginStates(); + } + + @Override + protected void onCleared() { + eventBus.removeListener(this); + } + + @Override + public void eventOccurred(Event e) { + if (e instanceof TransportStateEvent) { + TransportStateEvent t = (TransportStateEvent) e; + TransportId id = t.getTransportId(); + State state = t.getState(); + if (LOG.isLoggable(INFO)) { + LOG.info("TransportStateEvent: " + id + " is " + state); + } + MutableLiveData liveData = getPluginLiveData(id); + liveData.postValue(state); + } + } + + void onSwitchClicked(TransportId t, boolean isChecked) { + if (isChecked) tryToEnablePlugin(t); + else setPluginEnabled(t, false); + } + + LiveData getPluginState(TransportId t) { + return getPluginLiveData(t); + } + + private void tryToEnablePlugin(TransportId id) { + if (id.equals(TorConstants.ID)) { + int reasons = getReasonsDisabled(id); + if (reasons == 0 || reasons == REASON_USER) { + setPluginEnabled(id, true); + } else { + showTorSettingsDialog(reasons); + } + } else { + setPluginEnabled(id, true); + } + } + + private void initialisePluginStates() { + for (TransportId t : TRANSPORT_IDS) { + MutableLiveData liveData = getPluginLiveData(t); + liveData.setValue(getTransportState(t)); + } + } + + private State getTransportState(TransportId id) { + Plugin plugin = pluginManager.getPlugin(id); + return plugin == null ? STARTING_STOPPING : plugin.getState(); + } + + private MutableLiveData getPluginLiveData(TransportId t) { + if (t.equals(TorConstants.ID)) { + return torPluginState; + } else if (t.equals(LanTcpConstants.ID)) { + return wifiPluginState; + } else if (t.equals(BluetoothConstants.ID)) { + return btPluginState; + } else { + throw new IllegalArgumentException(); + } + } + + private int getReasonsDisabled(TransportId id) { + Plugin plugin = pluginManager.getPlugin(id); + return plugin == null ? 0 : plugin.getReasonsDisabled(); + } + + private void setPluginEnabled(TransportId t, boolean enabled) { + pluginManager.setPluginEnabled(t, enabled); + } + + private void setTorEnabled(boolean battery, boolean mobileData, + boolean location) { + Settings s = new Settings(); + s.putBoolean(PREF_PLUGIN_ENABLE, true); + if (battery) s.putBoolean(PREF_TOR_ONLY_WHEN_CHARGING, false); + if (mobileData) s.putBoolean(PREF_TOR_MOBILE, true); + if (location) s.putInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_WITH_BRIDGES); + dbExecutor.execute(() -> { + try { + settingsManager.mergeSettings(s, TorConstants.ID.getString()); + } catch (DbException e) { + logException(LOG, WARNING, e); + } + }); + } + + private void showTorSettingsDialog(int reasonsDisabled) { + boolean battery = (reasonsDisabled & REASON_BATTERY) != 0; + boolean mobileData = (reasonsDisabled & REASON_MOBILE_DATA) != 0; + boolean location = (reasonsDisabled & REASON_COUNTRY_BLOCKED) != 0; + + StringBuilder s = new StringBuilder(); + if (location) { + s.append("\t\u2022 "); + s.append(app.getString(R.string.tor_override_network_setting, + getCountryDisplayName(locationUtils.getCurrentCountry()))); + s.append('\n'); + } + if (mobileData) { + s.append("\t\u2022 "); + s.append(app.getString(R.string.tor_override_mobile_data_setting)); + s.append('\n'); + } + if (battery) { + s.append("\t\u2022 "); + s.append(app.getString(R.string.tor_only_when_charging_title)); + s.append('\n'); + } + String message = app.getString( + R.string.tor_override_settings_body, s.toString()); + + AlertDialog.Builder b = + new AlertDialog.Builder(app, R.style.BriarDialogTheme); + b.setTitle(R.string.tor_override_settings_title); + b.setIcon(getDialogIcon(app, R.drawable.ic_settings_black_24dp)); + b.setMessage(message); + b.setPositiveButton(R.string.tor_override_settings_confirm, + (dialog, which) -> + setTorEnabled(battery, mobileData, location)); + b.setNegativeButton(R.string.cancel, (dialog, which) -> + dialog.dismiss()); + b.show(); + } +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java index 07dd5a327..79879580d 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java @@ -20,6 +20,8 @@ import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; +import org.briarproject.bramble.api.plugin.BluetoothConstants; +import org.briarproject.bramble.api.plugin.LanTcpConstants; import org.briarproject.bramble.api.plugin.TorConstants; import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.SettingsManager; @@ -113,7 +115,15 @@ public class SettingsFragment extends PreferenceFragmentCompat "pref_key_lock_timeout"; public static final String NOTIFY_SIGN_IN = "pref_key_notify_sign_in"; + private static final String BT_NAMESPACE = + BluetoothConstants.ID.getString(); + private static final String BT_ENABLE = "pref_key_bluetooth"; + + private static final String WIFI_NAMESPACE = LanTcpConstants.ID.getString(); + private static final String WIFI_ENABLE = "pref_key_wifi"; + private static final String TOR_NAMESPACE = TorConstants.ID.getString(); + private static final String TOR_ENABLE = "pref_key_tor_enable"; private static final String TOR_NETWORK = "pref_key_tor_network"; private static final String TOR_MOBILE = "pref_key_tor_mobile_data"; private static final String TOR_ONLY_WHEN_CHARGING = @@ -124,6 +134,9 @@ public class SettingsFragment extends PreferenceFragmentCompat private SettingsActivity listener; private ListPreference language; + private SwitchPreference enableBluetooth; + private SwitchPreference enableWifi; + private SwitchPreference enableTor; private ListPreference torNetwork; private SwitchPreference torMobile; private SwitchPreference torOnlyWhenCharging; @@ -138,7 +151,7 @@ public class SettingsFragment extends PreferenceFragmentCompat private Preference notifySound; // Fields that are accessed from background threads must be volatile - private volatile Settings settings, torSettings; + private volatile Settings settings, btSettings, wifiSettings, torSettings; private volatile boolean settingsLoaded = false; @Inject @@ -167,6 +180,9 @@ public class SettingsFragment extends PreferenceFragmentCompat language = findPreference(LANGUAGE); setLanguageEntries(); ListPreference theme = findPreference("pref_key_theme"); + enableBluetooth = findPreference(BT_ENABLE); + enableWifi = findPreference(WIFI_ENABLE); + enableTor = findPreference(TOR_ENABLE); torNetwork = findPreference(TOR_NETWORK); torMobile = findPreference(TOR_MOBILE); torOnlyWhenCharging = findPreference(TOR_ONLY_WHEN_CHARGING); @@ -198,6 +214,9 @@ public class SettingsFragment extends PreferenceFragmentCompat } return true; }); + enableBluetooth.setOnPreferenceChangeListener(this); + enableWifi.setOnPreferenceChangeListener(this); + enableTor.setOnPreferenceChangeListener(this); torNetwork.setOnPreferenceChangeListener(this); torMobile.setOnPreferenceChangeListener(this); torOnlyWhenCharging.setOnPreferenceChangeListener(this); @@ -339,8 +358,9 @@ public class SettingsFragment extends PreferenceFragmentCompat try { long start = now(); settings = settingsManager.getSettings(SETTINGS_NAMESPACE); - torSettings = migrateTorSettings( - settingsManager.getSettings(TOR_NAMESPACE)); + btSettings = settingsManager.getSettings(BT_NAMESPACE); + wifiSettings = settingsManager.getSettings(WIFI_NAMESPACE); + torSettings = settingsManager.getSettings(TOR_NAMESPACE); settingsLoaded = true; logDuration(LOG, "Loading settings", start); displaySettings(); @@ -368,6 +388,18 @@ public class SettingsFragment extends PreferenceFragmentCompat // due to events, we might try to display before a load completed if (!settingsLoaded) return; + boolean btEnabledSetting = + btSettings.getBoolean(PREF_PLUGIN_ENABLE, false); + enableBluetooth.setChecked(btEnabledSetting); + + boolean wifiEnabledSetting = + wifiSettings.getBoolean(PREF_PLUGIN_ENABLE, false); + enableWifi.setChecked(wifiEnabledSetting); + + boolean torEnabledSetting = + torSettings.getBoolean(PREF_PLUGIN_ENABLE, true); + enableTor.setChecked(torEnabledSetting); + int torNetworkSetting = torSettings.getInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_AUTOMATIC); torNetwork.setValue(Integer.toString(torNetworkSetting)); @@ -439,6 +471,9 @@ public class SettingsFragment extends PreferenceFragmentCompat // preferences partly needed here, because they have their own logic // - pref_key_lock (screenLock -> displayScreenLockSetting()) // - pref_key_lock_timeout (screenLockTimeout) + enableBluetooth.setEnabled(enabled); + enableWifi.setEnabled(enabled); + enableTor.setEnabled(enabled); torNetwork.setEnabled(enabled); torMobile.setEnabled(enabled); torOnlyWhenCharging.setEnabled(enabled); @@ -540,6 +575,15 @@ public class SettingsFragment extends PreferenceFragmentCompat if (!language.getValue().equals(newValue)) languageChanged((String) newValue); return false; + } else if (preference == enableBluetooth) { + boolean btSetting = (Boolean) newValue; + storeBluetoothSetting(btSetting); + } else if (preference == enableWifi) { + boolean wifiSetting = (Boolean) newValue; + storeWifiSetting(wifiSetting); + } else if (preference == enableTor) { + boolean torEnabledSetting = (Boolean) newValue; + storeTorEnabledSetting(torEnabledSetting); } else if (preference == torNetwork) { int torNetworkSetting = Integer.valueOf((String) newValue); storeTorNetworkSetting(torNetworkSetting); @@ -603,6 +647,12 @@ public class SettingsFragment extends PreferenceFragmentCompat builder.show(); } + private void storeTorEnabledSetting(boolean torEnabledSetting) { + Settings s = new Settings(); + s.putBoolean(PREF_PLUGIN_ENABLE, torEnabledSetting); + mergeSettings(s, TOR_NAMESPACE); + } + private void storeTorNetworkSetting(int torNetworkSetting) { Settings s = new Settings(); s.putInt(PREF_TOR_NETWORK, torNetworkSetting); @@ -621,6 +671,18 @@ public class SettingsFragment extends PreferenceFragmentCompat mergeSettings(s, TOR_NAMESPACE); } + private void storeBluetoothSetting(boolean btSetting) { + Settings s = new Settings(); + s.putBoolean(PREF_PLUGIN_ENABLE, btSetting); + mergeSettings(s, BT_NAMESPACE); + } + + private void storeWifiSetting(boolean wifiSetting) { + Settings s = new Settings(); + s.putBoolean(PREF_PLUGIN_ENABLE, wifiSetting); + mergeSettings(s, WIFI_NAMESPACE); + } + private void storeSettings(Settings s) { mergeSettings(s, SETTINGS_NAMESPACE); } @@ -679,6 +741,14 @@ public class SettingsFragment extends PreferenceFragmentCompat LOG.info("Settings updated"); settings = s.getSettings(); displaySettings(); + } else if (namespace.equals(BT_NAMESPACE)) { + LOG.info("Bluetooth settings updated"); + btSettings = s.getSettings(); + displaySettings(); + } else if (namespace.equals(WIFI_NAMESPACE)) { + LOG.info("Wifi settings updated"); + wifiSettings = s.getSettings(); + displaySettings(); } else if (namespace.equals(TOR_NAMESPACE)) { LOG.info("Tor settings updated"); torSettings = migrateTorSettings(s.getSettings()); diff --git a/briar-android/src/main/res/drawable/ic_baseline_drag_handle.xml b/briar-android/src/main/res/drawable/ic_baseline_drag_handle.xml deleted file mode 100644 index de7bbda0a..000000000 --- a/briar-android/src/main/res/drawable/ic_baseline_drag_handle.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/briar-android/src/main/res/layout/navigation_menu.xml b/briar-android/src/main/res/layout/navigation_menu.xml index f23d4bda9..4a7a32c4f 100644 --- a/briar-android/src/main/res/layout/navigation_menu.xml +++ b/briar-android/src/main/res/layout/navigation_menu.xml @@ -1,12 +1,59 @@ - + - \ No newline at end of file + + + + + + + + + + + diff --git a/briar-android/src/main/res/layout/navigation_menu_collapsed.xml b/briar-android/src/main/res/layout/navigation_menu_collapsed.xml deleted file mode 100644 index 11daa10fc..000000000 --- a/briar-android/src/main/res/layout/navigation_menu_collapsed.xml +++ /dev/null @@ -1,165 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/briar-android/src/main/res/layout/navigation_menu_expanded.xml b/briar-android/src/main/res/layout/navigation_menu_expanded.xml deleted file mode 100644 index 1573a9917..000000000 --- a/briar-android/src/main/res/layout/navigation_menu_expanded.xml +++ /dev/null @@ -1,183 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/briar-android/src/main/res/layout/transports_list.xml b/briar-android/src/main/res/layout/transports_list.xml new file mode 100644 index 000000000..a484522c7 --- /dev/null +++ b/briar-android/src/main/res/layout/transports_list.xml @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index a67955b1f..ef8a56111 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -70,10 +70,7 @@ Sign Out - Connections - Internet - Tor - Nearby + Internet Bluetooth Wi-Fi @@ -440,15 +437,19 @@ Connections - Connection method for Internet (Tor) + Connect to contacts via Bluetooth + Connect to contacts on the same Wi-Fi network + Connect to contacts via the Internet + All connections go through the Tor network for privacy + Connection method for Tor network Automatic based on location - Use Tor without bridges - Use Tor with bridges - Don\'t connect - + Use Tor network without bridges + Use Tor network with bridges + Don\'t connect to the Internet + Automatic: %1$s (in %2$s) Use mobile data - Connect via Internet (Tor) only when charging + Connect to the Internet only when charging Disables Internet connection when device is running on battery @@ -571,9 +572,9 @@ Change Settings - Turning on Tor will change the following settings:\n\n%1$s + Turning on connections via the Internet will change the following settings:\n\n%1$s Don\'t use mobile data - Don\'t connect to Internet (Tor) in %1$s + Don\'t connect to the Internet in %1$s Change diff --git a/briar-android/src/main/res/xml/settings.xml b/briar-android/src/main/res/xml/settings.xml index 3a5781a8d..21acdd98f 100644 --- a/briar-android/src/main/res/xml/settings.xml +++ b/briar-android/src/main/res/xml/settings.xml @@ -29,8 +29,34 @@ android:layout="@layout/preferences_category" android:title="@string/network_settings_title"> + + + + + + Date: Mon, 27 Apr 2020 16:34:49 +0100 Subject: [PATCH 65/69] Remove unused code, fix a null safety bug. --- .../android/navdrawer/NavDrawerViewModel.java | 6 - .../android/navdrawer/PluginViewModel.java | 155 ++---------------- 2 files changed, 16 insertions(+), 145 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java index 76c1aeb83..37f26b6e4 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java @@ -5,10 +5,6 @@ import android.app.Application; import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; -import org.briarproject.bramble.api.plugin.BluetoothConstants; -import org.briarproject.bramble.api.plugin.LanTcpConstants; -import org.briarproject.bramble.api.plugin.TorConstants; -import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.SettingsManager; @@ -39,8 +35,6 @@ public class NavDrawerViewModel extends AndroidViewModel { getLogger(NavDrawerViewModel.class.getName()); private static final String EXPIRY_DATE_WARNING = "expiryDateWarning"; - static final TransportId[] TRANSPORT_IDS = - {TorConstants.ID, LanTcpConstants.ID, BluetoothConstants.ID}; @DatabaseExecutor private final Executor dbExecutor; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewModel.java index f67736ba4..1a4a9b7a7 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewModel.java @@ -1,9 +1,5 @@ package org.briarproject.briar.android.navdrawer; -import android.app.Application; - -import org.briarproject.bramble.api.db.DatabaseExecutor; -import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventListener; @@ -16,51 +12,27 @@ import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.TorConstants; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.event.TransportStateEvent; -import org.briarproject.bramble.api.settings.Settings; -import org.briarproject.bramble.api.settings.SettingsManager; -import org.briarproject.bramble.api.system.LocationUtils; -import org.briarproject.briar.R; -import java.util.concurrent.Executor; import java.util.logging.Logger; import javax.inject.Inject; -import androidx.appcompat.app.AlertDialog; -import androidx.lifecycle.AndroidViewModel; +import androidx.annotation.Nullable; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; import static java.util.logging.Level.INFO; -import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; -import static org.briarproject.bramble.api.plugin.Plugin.PREF_PLUGIN_ENABLE; -import static org.briarproject.bramble.api.plugin.Plugin.REASON_USER; import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; -import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE; -import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK; -import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_WITH_BRIDGES; -import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ONLY_WHEN_CHARGING; -import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY; -import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED; -import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA; -import static org.briarproject.bramble.util.LogUtils.logException; -import static org.briarproject.briar.android.navdrawer.NavDrawerViewModel.TRANSPORT_IDS; -import static org.briarproject.briar.android.util.UiUtils.getCountryDisplayName; -import static org.briarproject.briar.android.util.UiUtils.getDialogIcon; @NotNullByDefault -public class PluginViewModel extends AndroidViewModel implements EventListener { +public class PluginViewModel extends ViewModel implements EventListener { private static final Logger LOG = getLogger(PluginViewModel.class.getName()); - private final Application app; - @DatabaseExecutor - private final Executor dbExecutor; - private final SettingsManager settingsManager; private final PluginManager pluginManager; - private final LocationUtils locationUtils; private final EventBus eventBus; private final MutableLiveData torPluginState = @@ -71,18 +43,13 @@ public class PluginViewModel extends AndroidViewModel implements EventListener { new MutableLiveData<>(); @Inject - PluginViewModel(Application app, @DatabaseExecutor Executor dbExecutor, - SettingsManager settingsManager, PluginManager pluginManager, - LocationUtils locationUtils, EventBus eventBus) { - super(app); - this.app = app; - this.dbExecutor = dbExecutor; - this.settingsManager = settingsManager; + PluginViewModel(PluginManager pluginManager, EventBus eventBus) { this.pluginManager = pluginManager; - this.locationUtils = locationUtils; this.eventBus = eventBus; eventBus.addListener(this); - initialisePluginStates(); + torPluginState.setValue(getTransportState(TorConstants.ID)); + wifiPluginState.setValue(getTransportState(LanTcpConstants.ID)); + btPluginState.setValue(getTransportState(BluetoothConstants.ID)); } @Override @@ -100,37 +67,14 @@ public class PluginViewModel extends AndroidViewModel implements EventListener { LOG.info("TransportStateEvent: " + id + " is " + state); } MutableLiveData liveData = getPluginLiveData(id); - liveData.postValue(state); + if (liveData != null) liveData.postValue(state); } } - void onSwitchClicked(TransportId t, boolean isChecked) { - if (isChecked) tryToEnablePlugin(t); - else setPluginEnabled(t, false); - } - LiveData getPluginState(TransportId t) { - return getPluginLiveData(t); - } - - private void tryToEnablePlugin(TransportId id) { - if (id.equals(TorConstants.ID)) { - int reasons = getReasonsDisabled(id); - if (reasons == 0 || reasons == REASON_USER) { - setPluginEnabled(id, true); - } else { - showTorSettingsDialog(reasons); - } - } else { - setPluginEnabled(id, true); - } - } - - private void initialisePluginStates() { - for (TransportId t : TRANSPORT_IDS) { - MutableLiveData liveData = getPluginLiveData(t); - liveData.setValue(getTransportState(t)); - } + LiveData liveData = getPluginLiveData(t); + if (liveData == null) throw new IllegalArgumentException(); + return liveData; } private State getTransportState(TransportId id) { @@ -138,78 +82,11 @@ public class PluginViewModel extends AndroidViewModel implements EventListener { return plugin == null ? STARTING_STOPPING : plugin.getState(); } + @Nullable private MutableLiveData getPluginLiveData(TransportId t) { - if (t.equals(TorConstants.ID)) { - return torPluginState; - } else if (t.equals(LanTcpConstants.ID)) { - return wifiPluginState; - } else if (t.equals(BluetoothConstants.ID)) { - return btPluginState; - } else { - throw new IllegalArgumentException(); - } - } - - private int getReasonsDisabled(TransportId id) { - Plugin plugin = pluginManager.getPlugin(id); - return plugin == null ? 0 : plugin.getReasonsDisabled(); - } - - private void setPluginEnabled(TransportId t, boolean enabled) { - pluginManager.setPluginEnabled(t, enabled); - } - - private void setTorEnabled(boolean battery, boolean mobileData, - boolean location) { - Settings s = new Settings(); - s.putBoolean(PREF_PLUGIN_ENABLE, true); - if (battery) s.putBoolean(PREF_TOR_ONLY_WHEN_CHARGING, false); - if (mobileData) s.putBoolean(PREF_TOR_MOBILE, true); - if (location) s.putInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_WITH_BRIDGES); - dbExecutor.execute(() -> { - try { - settingsManager.mergeSettings(s, TorConstants.ID.getString()); - } catch (DbException e) { - logException(LOG, WARNING, e); - } - }); - } - - private void showTorSettingsDialog(int reasonsDisabled) { - boolean battery = (reasonsDisabled & REASON_BATTERY) != 0; - boolean mobileData = (reasonsDisabled & REASON_MOBILE_DATA) != 0; - boolean location = (reasonsDisabled & REASON_COUNTRY_BLOCKED) != 0; - - StringBuilder s = new StringBuilder(); - if (location) { - s.append("\t\u2022 "); - s.append(app.getString(R.string.tor_override_network_setting, - getCountryDisplayName(locationUtils.getCurrentCountry()))); - s.append('\n'); - } - if (mobileData) { - s.append("\t\u2022 "); - s.append(app.getString(R.string.tor_override_mobile_data_setting)); - s.append('\n'); - } - if (battery) { - s.append("\t\u2022 "); - s.append(app.getString(R.string.tor_only_when_charging_title)); - s.append('\n'); - } - String message = app.getString( - R.string.tor_override_settings_body, s.toString()); - - AlertDialog.Builder b = - new AlertDialog.Builder(app, R.style.BriarDialogTheme); - b.setTitle(R.string.tor_override_settings_title); - b.setIcon(getDialogIcon(app, R.drawable.ic_settings_black_24dp)); - b.setMessage(message); - b.setPositiveButton(R.string.tor_override_settings_confirm, - (dialog, which) -> - setTorEnabled(battery, mobileData, location)); - b.setNegativeButton(R.string.cancel, (dialog, which) -> - dialog.dismiss()); - b.show(); + if (t.equals(TorConstants.ID)) return torPluginState; + else if (t.equals(LanTcpConstants.ID)) return wifiPluginState; + else if (t.equals(BluetoothConstants.ID)) return btPluginState; + else return null; } } From ef5e2dad72ba4e9c16755dde5bfcffd84a317c29 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 27 Apr 2020 17:54:41 +0100 Subject: [PATCH 66/69] Make inner class just a holder for resources. --- .../android/navdrawer/NavDrawerActivity.java | 49 ++++++++++--------- .../android/navdrawer/PluginViewModel.java | 12 ++--- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java index 1ea8f9772..3cd9f53a1 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java @@ -43,6 +43,7 @@ import java.util.logging.Logger; import javax.inject.Inject; +import androidx.annotation.ColorRes; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -409,12 +410,13 @@ public class NavDrawerActivity extends BriarActivity implements Transport t = getItem(position); ImageView icon = view.findViewById(R.id.imageView); - icon.setImageDrawable(ContextCompat - .getDrawable(NavDrawerActivity.this, t.iconId)); - icon.setColorFilter(getIconColour(t.state)); + icon.setImageDrawable(ContextCompat.getDrawable( + NavDrawerActivity.this, t.iconDrawable)); + icon.setColorFilter(ContextCompat.getColor( + NavDrawerActivity.this, t.iconColor)); TextView text = view.findViewById(R.id.textView); - text.setText(getString(t.textId)); + text.setText(getString(t.label)); return view; } @@ -428,23 +430,19 @@ public class NavDrawerActivity extends BriarActivity implements R.drawable.transport_bt, R.string.transport_bt)); } - private int getIconColour(State state) { - int colorRes; - if (state == ACTIVE) { - colorRes = R.color.briar_green_light; - } else if (state == ENABLING) { - colorRes = R.color.briar_yellow; - } else { - colorRes = android.R.color.tertiary_text_light; - } - return ContextCompat.getColor(this, colorRes); + @ColorRes + private int getIconColor(State state) { + if (state == ACTIVE) return R.color.briar_green_light; + else if (state == ENABLING) return R.color.briar_yellow; + else return android.R.color.tertiary_text_light; } - private Transport createTransport(TransportId id, @DrawableRes int iconId, - @StringRes int textId) { - Transport transport = new Transport(iconId, textId); + private Transport createTransport(TransportId id, + @DrawableRes int iconDrawable, @StringRes int label) { + int iconColor = getIconColor(STARTING_STOPPING); + Transport transport = new Transport(iconDrawable, label, iconColor); pluginViewModel.getPluginState(id).observe(this, state -> { - transport.state = state; + transport.iconColor = getIconColor(state); transportsAdapter.notifyDataSetChanged(); }); return transport; @@ -453,15 +451,18 @@ public class NavDrawerActivity extends BriarActivity implements private static class Transport { @DrawableRes - private final int iconId; + private final int iconDrawable; @StringRes - private final int textId; + private final int label; - private State state = STARTING_STOPPING; + @ColorRes + private int iconColor; - private Transport(@DrawableRes int iconId, @StringRes int textId) { - this.iconId = iconId; - this.textId = textId; + private Transport(@DrawableRes int iconDrawable, @StringRes int label, + @ColorRes int iconColor) { + this.iconDrawable = iconDrawable; + this.label = label; + this.iconColor = iconColor; } } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewModel.java index 1a4a9b7a7..8badce8c4 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/PluginViewModel.java @@ -71,8 +71,8 @@ public class PluginViewModel extends ViewModel implements EventListener { } } - LiveData getPluginState(TransportId t) { - LiveData liveData = getPluginLiveData(t); + LiveData getPluginState(TransportId id) { + LiveData liveData = getPluginLiveData(id); if (liveData == null) throw new IllegalArgumentException(); return liveData; } @@ -83,10 +83,10 @@ public class PluginViewModel extends ViewModel implements EventListener { } @Nullable - private MutableLiveData getPluginLiveData(TransportId t) { - if (t.equals(TorConstants.ID)) return torPluginState; - else if (t.equals(LanTcpConstants.ID)) return wifiPluginState; - else if (t.equals(BluetoothConstants.ID)) return btPluginState; + private MutableLiveData getPluginLiveData(TransportId id) { + if (id.equals(TorConstants.ID)) return torPluginState; + else if (id.equals(LanTcpConstants.ID)) return wifiPluginState; + else if (id.equals(BluetoothConstants.ID)) return btPluginState; else return null; } } From 011d8e1df7ee3bb0c9d573b1949e01d70cc796b9 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Tue, 28 Apr 2020 16:07:52 +0100 Subject: [PATCH 67/69] Remove unused strings. --- briar-android/src/main/res/values/strings.xml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index ef8a56111..e4f64f7a8 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -570,13 +570,6 @@ Briar is locked Tap to unlock - - Change Settings - Turning on connections via the Internet will change the following settings:\n\n%1$s - Don\'t use mobile data - Don\'t connect to the Internet in %1$s - Change - From 033fd2d3b4af7149c5ec6830bbb60695f3d6fd41 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 25 Jun 2020 15:31:43 +0100 Subject: [PATCH 68/69] Update comments about migration periods, since this isn't merged yet. --- .../java/org/briarproject/bramble/plugin/tor/TorPlugin.java | 2 +- .../briarproject/briar/android/settings/SettingsFragment.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index 66a869d0c..374f221af 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -287,7 +287,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { bind(); } - // TODO: Remove after a reasonable migration period (added 2020-01-16) + // TODO: Remove after a reasonable migration period (added 2020-06-25) private Settings migrateSettings(Settings settings) { int network = settings.getInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_AUTOMATIC); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java index 79879580d..0c41e6fba 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java @@ -370,7 +370,7 @@ public class SettingsFragment extends PreferenceFragmentCompat }); } - // TODO: Remove after a reasonable migration period (added 2020-01-29) + // TODO: Remove after a reasonable migration period (added 2020-06-25) private Settings migrateTorSettings(Settings s) { int network = s.getInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_AUTOMATIC); if (network == PREF_TOR_NETWORK_NEVER) { From a03953563f783840ea91b0f6ddefaa54bd6d8cbd Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 25 Jun 2020 16:02:52 +0100 Subject: [PATCH 69/69] Show appropriate text for answered introduction requests. --- .../briar/android/conversation/ConversationVisitor.java | 6 +++++- briar-android/src/main/res/values/strings.xml | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationVisitor.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationVisitor.java index 3d4080f03..46468c030 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationVisitor.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationVisitor.java @@ -230,7 +230,11 @@ class ConversationVisitor implements R.layout.list_item_conversation_notice_out, text, r); } else { String text; - if (r.isContact()) { + if (r.wasAnswered()) { + text = ctx.getString( + R.string.introduction_request_answered_received, + contactName.getValue(), name); + } else if (r.isContact()) { text = ctx.getString( R.string.introduction_request_exists_received, contactName.getValue(), name); diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index e4f64f7a8..482b5ce1a 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -258,6 +258,7 @@ You have asked to introduce %1$s to %2$s. %1$s has asked to introduce you to %2$s. Do you want to add %2$s to your contact list? %1$s has asked to introduce you to %2$s, but %2$s is already in your contact list. Since %1$s might not know that, you can still respond: + %1$s has asked to introduce you to %2$s. You accepted the introduction to %1$s. Before %1$s gets added to your contacts, they need to accept the introduction as well. This might take some time. You declined the introduction to %1$s.