From 53889436fc896080f028f1098cb7b1682a221152 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Tue, 14 Jan 2020 12:18:24 +0000 Subject: [PATCH] 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 | 111 +++++++++++---- .../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, 389 insertions(+), 152 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 ec7bd1941..aedbd85e5 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 = getLocalInetAddresses(); 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 f36773615..10ac8e788 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 @@ -74,7 +74,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 d4d69ca56..b64fa054c 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.ServiceException; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.ConnectionManager; 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 0dec35bc5..8df0d40f6 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.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; @@ -36,6 +37,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; @@ -46,6 +49,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; @@ -68,9 +74,10 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { private final int maxLatency; 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; @@ -120,13 +127,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 @@ -154,7 +164,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(); @@ -172,7 +183,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 { @@ -181,14 +193,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); }); } @@ -217,34 +230,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"); backoff.reset(); if (connectionLimiter.contactConnectionOpened(conn)) 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 @@ -260,7 +275,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()); @@ -273,7 +288,8 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { String uuid = p.get(PROP_UUID); if (isNullOrEmpty(uuid)) return; ioExecutor.execute(() -> { - if (!isRunning() || !shouldAllowContactConnections()) return; + if (!shouldAllowContactConnections() || getState() != AVAILABLE) + return; if (!connectionLimiter.canOpenContactConnection()) return; DuplexTransportConnection d = createConnection(p); if (d != null) { @@ -317,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; @@ -336,7 +353,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); @@ -348,7 +365,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { logException(LOG, WARNING, e); return null; } - if (!isRunning()) { + if (getState() != AVAILABLE) { tryToClose(ss); return null; } @@ -362,7 +379,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; @@ -428,8 +445,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"); @@ -460,4 +476,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; } }