mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Provide more information about plugin states.
This commit is contained in:
@@ -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<InetAddress> 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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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> 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
|
||||
|
||||
@@ -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<SS> 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<SS> 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<SS> 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<SS> 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<SS> 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<SS> 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<SS> implements DuplexPlugin, EventListener {
|
||||
@Override
|
||||
public void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
|
||||
properties) {
|
||||
if (!isRunning() || !shouldAllowContactConnections()) return;
|
||||
if (!shouldAllowContactConnections() || getState() != AVAILABLE) return;
|
||||
backoff.increment();
|
||||
for (Pair<TransportProperties, ConnectionHandler> p : properties) {
|
||||
connect(p.getFirst(), p.getSecond());
|
||||
@@ -319,7 +333,8 @@ abstract class BluetoothPlugin<SS> 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<SS> 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<SS> 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<SS> 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<SS> 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<SS> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Pair<TransportProperties, ConnectionHandler>>
|
||||
properties) {
|
||||
if (!isRunning()) return;
|
||||
if (getState() != AVAILABLE) return;
|
||||
backoff.increment();
|
||||
for (Pair<TransportProperties, ConnectionHandler> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Integer, String> 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<Pair<TransportProperties, ConnectionHandler>>
|
||||
properties) {
|
||||
if (!isRunning()) return;
|
||||
if (getState() != AVAILABLE) return;
|
||||
backoff.increment();
|
||||
for (Pair<TransportProperties, ConnectionHandler> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user