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 577a773e7..d4a8a417b 100644
--- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPlugin.java
+++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPlugin.java
@@ -9,11 +9,11 @@ import android.net.wifi.WifiManager;
import org.briarproject.bramble.PoliteExecutor;
import org.briarproject.bramble.api.event.Event;
-import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.network.event.NetworkStatusEvent;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.PluginCallback;
+import org.briarproject.bramble.api.settings.Settings;
import java.io.IOException;
import java.net.InetAddress;
@@ -36,10 +36,11 @@ import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
+import static org.briarproject.bramble.api.plugin.TcpConstants.PREF_TCP_ENABLE;
import static org.briarproject.bramble.util.IoUtils.tryToClose;
@NotNullByDefault
-class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener {
+class AndroidLanTcpPlugin extends LanTcpPlugin {
private static final Logger LOG =
getLogger(AndroidLanTcpPlugin.class.getName());
@@ -83,7 +84,8 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener {
@Override
public void start() {
if (used.getAndSet(true)) throw new IllegalStateException();
- state.setStarted();
+ Settings settings = callback.getSettings();
+ state.setStarted(settings.getBoolean(PREF_TCP_ENABLE, false));
updateConnectionStatus();
}
@@ -136,6 +138,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener {
@Override
public void eventOccurred(Event e) {
+ super.eventOccurred(e);
if (e instanceof NetworkStatusEvent) updateConnectionStatus();
}
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java
index 785aee2b3..915dfbbab 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java
@@ -10,4 +10,7 @@ public interface BluetoothConstants {
String PROP_UUID = "uuid";
String PREF_BT_ENABLE = "enable";
+
+ // Reason code returned by Plugin#getReasonDisabled()
+ int REASON_NO_BT_ADAPTER = 2;
}
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java
index b7247967c..ee26500f1 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/Plugin.java
@@ -35,12 +35,18 @@ public interface Plugin {
}
/**
- * Reason code returned by {@link #getReasonDisabled()} ()} to indicate
- * that the plugin is disabled because it has not been started or has been
+ * Reason code returned by {@link #getReasonDisabled()} to indicate that
+ * the plugin is disabled because it has not been started or has been
* stopped.
*/
int REASON_STARTING_STOPPING = 0;
+ /**
+ * Reason code returned by {@link #getReasonDisabled()} to indicate that
+ * the plugin has been disabled by the user.
+ */
+ int REASON_USER = 1;
+
/**
* Returns the plugin's transport identifier.
*/
@@ -75,9 +81,9 @@ public interface Plugin {
* Returns an integer code indicating why the plugin is
* {@link State#DISABLED disabled}, or -1 if the plugin is not disabled.
*
- * The codes used are plugin-specific, except the generic code
- * {@link #REASON_STARTING_STOPPING}, which may be used by
- * any plugin.
+ * The codes used are plugin-specific, except the generic codes
+ * {@link #REASON_STARTING_STOPPING} and {@link #REASON_USER}, which may
+ * be used by any plugin.
*/
int getReasonDisabled();
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TcpConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TcpConstants.java
new file mode 100644
index 000000000..503f2cb88
--- /dev/null
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TcpConstants.java
@@ -0,0 +1,6 @@
+package org.briarproject.bramble.api.plugin;
+
+public interface TcpConstants {
+
+ String PREF_TCP_ENABLE = "enable";
+}
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java
index 2d808eaa9..727dab4b4 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/TorConstants.java
@@ -13,6 +13,7 @@ public interface TorConstants {
int CONNECT_TO_PROXY_TIMEOUT = 5000; // Milliseconds
int EXTRA_SOCKET_TIMEOUT = 30000; // Milliseconds
+ String PREF_TOR_ENABLE = "enable";
String PREF_TOR_NETWORK = "network2";
String PREF_TOR_PORT = "port";
String PREF_TOR_MOBILE = "useMobileData";
@@ -23,7 +24,7 @@ public interface TorConstants {
int PREF_TOR_NETWORK_WITH_BRIDGES = 2;
int PREF_TOR_NETWORK_NEVER = 3;
- int REASON_USER = 1;
+ // Reason codes returned by Plugin#getReasonDisabled()
int REASON_BATTERY = 2;
int REASON_MOBILE_DATA = 3;
int REASON_COUNTRY_BLOCKED = 4;
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java
index e15a3e623..a8c53b417 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
@@ -9,6 +9,7 @@ import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStoppedListeningEvent;
+import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
@@ -48,6 +49,7 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENABLE;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
+import static org.briarproject.bramble.api.plugin.BluetoothConstants.REASON_NO_BT_ADAPTER;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED;
@@ -76,7 +78,6 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener {
protected final PluginState state = new PluginState();
- private volatile boolean contactConnections = false;
private volatile String contactConnectionsUuid = null;
abstract void initialiseAdapter() throws IOException;
@@ -126,16 +127,18 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener {
LOG.info("Bluetooth enabled");
// We may not have been able to get the local address before
ioExecutor.execute(this::updateProperties);
- if (shouldAllowContactConnections()) bind();
- callback.pluginStateChanged(getState());
+ if (getState() == INACTIVE) bind();
}
void onAdapterDisabled() {
LOG.info("Bluetooth disabled");
connectionLimiter.allConnectionsClosed();
// The server socket may not have been closed automatically
- tryToClose(state.clearServerSocket());
- callback.pluginStateChanged(getState());
+ SS ss = state.clearServerSocket();
+ if (ss != null) {
+ LOG.info("Closing server socket");
+ tryToClose(ss);
+ }
}
@Override
@@ -160,29 +163,22 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener {
try {
initialiseAdapter();
} catch (IOException e) {
+ state.setNoAdapter();
throw new PluginException(e);
}
updateProperties();
- state.setStarted();
- loadSettings(callback.getSettings());
- if (shouldAllowContactConnections()) {
+ Settings settings = callback.getSettings();
+ boolean enabledByUser = settings.getBoolean(PREF_BT_ENABLE, false);
+ state.setStarted(enabledByUser);
+ if (enabledByUser) {
if (isAdapterEnabled()) bind();
else enableAdapter();
}
}
- private void loadSettings(Settings settings) {
- contactConnections = settings.getBoolean(PREF_BT_ENABLE, false);
- }
-
- private boolean shouldAllowContactConnections() {
- return contactConnections;
- }
-
private void bind() {
ioExecutor.execute(() -> {
- if (!shouldAllowContactConnections() || getState() != ACTIVE)
- return;
+ if (getState() != INACTIVE) return;
// Bind a server socket to accept connections from contacts
SS ss;
try {
@@ -191,8 +187,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener {
logException(LOG, WARNING, e);
return;
}
- if (!shouldAllowContactConnections() ||
- !state.setServerSocket(ss)) {
+ if (!state.setServerSocket(ss)) {
LOG.info("Closing redundant server socket");
tryToClose(ss);
return;
@@ -259,7 +254,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener {
@Override
public int getReasonDisabled() {
- return getState() == DISABLED ? REASON_STARTING_STOPPING : -1;
+ return state.getReasonDisabled();
}
@Override
@@ -275,7 +270,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener {
@Override
public void poll(Collection>
properties) {
- if (!shouldAllowContactConnections() || getState() != ACTIVE) return;
+ if (getState() != ACTIVE) return;
backoff.increment();
for (Pair p : properties) {
connect(p.getFirst(), p.getSecond());
@@ -288,8 +283,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener {
String uuid = p.get(PROP_UUID);
if (isNullOrEmpty(uuid)) return;
ioExecutor.execute(() -> {
- if (!shouldAllowContactConnections() || getState() != ACTIVE)
- return;
+ if (getState() != ACTIVE) return;
if (!connectionLimiter.canOpenContactConnection()) return;
DuplexTransportConnection d = createConnection(p);
if (d != null) {
@@ -333,8 +327,7 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener {
@Override
public DuplexTransportConnection createConnection(TransportProperties p) {
- if (!shouldAllowContactConnections() || getState() != ACTIVE)
- return null;
+ if (getState() != ACTIVE) return null;
if (!connectionLimiter.canOpenContactConnection()) return null;
String address = p.get(PROP_ADDRESS);
if (isNullOrEmpty(address)) return null;
@@ -439,16 +432,17 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener {
}
}
+ @IoExecutor
private void onSettingsUpdated(Settings settings) {
- boolean wasAllowed = shouldAllowContactConnections();
- loadSettings(settings);
- boolean isAllowed = shouldAllowContactConnections();
- if (wasAllowed && !isAllowed) {
- LOG.info("Contact connections disabled");
- tryToClose(state.clearServerSocket());
+ boolean enabledByUser = settings.getBoolean(PREF_BT_ENABLE, false);
+ SS ss = state.setEnabledByUser(enabledByUser);
+ State s = getState();
+ if (ss != null) {
+ LOG.info("Disabled by user, closing server socket");
+ tryToClose(ss);
disableAdapterIfEnabledByUs();
- } else if (!wasAllowed && isAllowed) {
- LOG.info("Contact connections enabled");
+ } else if (s == INACTIVE) {
+ LOG.info("Enabled by user, opening server socket");
if (isAdapterEnabled()) bind();
else enableAdapter();
}
@@ -482,13 +476,18 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener {
protected class PluginState {
@GuardedBy("this")
- private boolean started = false, stopped = false;
+ private boolean started = false,
+ stopped = false,
+ noAdapter = false,
+ enabledByUser = false;
+
@GuardedBy("this")
@Nullable
private SS serverSocket = null;
- synchronized void setStarted() {
+ synchronized void setStarted(boolean enabledByUser) {
started = true;
+ this.enabledByUser = enabledByUser;
callback.pluginStateChanged(getState());
}
@@ -501,6 +500,23 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener {
return ss;
}
+ synchronized void setNoAdapter() {
+ noAdapter = true;
+ callback.pluginStateChanged(getState());
+ }
+
+ @Nullable
+ synchronized SS setEnabledByUser(boolean enabledByUser) {
+ this.enabledByUser = enabledByUser;
+ SS ss = null;
+ if (!enabledByUser) {
+ ss = serverSocket;
+ serverSocket = null;
+ }
+ callback.pluginStateChanged(getState());
+ return ss;
+ }
+
synchronized boolean setServerSocket(SS ss) {
if (stopped || serverSocket != null) return false;
serverSocket = ss;
@@ -512,12 +528,19 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener {
synchronized SS clearServerSocket() {
SS ss = serverSocket;
serverSocket = null;
+ callback.pluginStateChanged(getState());
return ss;
}
synchronized State getState() {
- if (!started || stopped) return DISABLED;
- return isAdapterEnabled() ? ACTIVE : INACTIVE;
+ if (!started || stopped || !enabledByUser) return DISABLED;
+ return serverSocket == null ? INACTIVE : ACTIVE;
+ }
+
+ synchronized int getReasonDisabled() {
+ if (noAdapter && !stopped) return REASON_NO_BT_ADAPTER;
+ if (!started || stopped) return REASON_STARTING_STOPPING;
+ return enabledByUser ? -1 : REASON_USER;
}
}
}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginFactory.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginFactory.java
index caa4c6a2e..7b0d20463 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginFactory.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPluginFactory.java
@@ -1,5 +1,6 @@
package org.briarproject.bramble.plugin.tcp;
+import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BackoffFactory;
@@ -25,11 +26,13 @@ public class LanTcpPluginFactory implements DuplexPluginFactory {
private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor;
+ private final EventBus eventBus;
private final BackoffFactory backoffFactory;
- public LanTcpPluginFactory(Executor ioExecutor,
+ public LanTcpPluginFactory(Executor ioExecutor, EventBus eventBus,
BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor;
+ this.eventBus = eventBus;
this.backoffFactory = backoffFactory;
}
@@ -47,7 +50,9 @@ public class LanTcpPluginFactory implements DuplexPluginFactory {
public DuplexPlugin createPlugin(PluginCallback callback) {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
- return new LanTcpPlugin(ioExecutor, backoff, callback, MAX_LATENCY,
- MAX_IDLE_TIME);
+ LanTcpPlugin plugin = new LanTcpPlugin(ioExecutor, backoff, callback,
+ MAX_LATENCY, MAX_IDLE_TIME);
+ eventBus.addListener(plugin);
+ return plugin;
}
}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java
index d4713fecf..215a47ea1 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java
@@ -3,7 +3,10 @@ package org.briarproject.bramble.plugin.tcp;
import org.briarproject.bramble.PoliteExecutor;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.data.BdfList;
+import org.briarproject.bramble.api.event.Event;
+import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
+import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
@@ -15,6 +18,8 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
+import org.briarproject.bramble.api.settings.Settings;
+import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
import java.io.IOException;
import java.net.InetAddress;
@@ -46,6 +51,7 @@ import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED;
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
+import static org.briarproject.bramble.api.plugin.TcpConstants.PREF_TCP_ENABLE;
import static org.briarproject.bramble.util.IoUtils.tryToClose;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress;
@@ -53,7 +59,7 @@ import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
-abstract class TcpPlugin implements DuplexPlugin {
+abstract class TcpPlugin implements DuplexPlugin, EventListener {
private static final Logger LOG = getLogger(TcpPlugin.class.getName());
@@ -119,7 +125,8 @@ abstract class TcpPlugin implements DuplexPlugin {
@Override
public void start() {
if (used.getAndSet(true)) throw new IllegalStateException();
- state.setStarted();
+ Settings settings = callback.getSettings();
+ state.setStarted(settings.getBoolean(PREF_TCP_ENABLE, false));
bind();
}
@@ -197,7 +204,7 @@ abstract class TcpPlugin implements DuplexPlugin {
@Override
public int getReasonDisabled() {
- return getState() == DISABLED ? REASON_STARTING_STOPPING : -1;
+ return state.getReasonDisabled();
}
@Override
@@ -331,18 +338,43 @@ abstract class TcpPlugin implements DuplexPlugin {
}
}
+ @Override
+ public void eventOccurred(Event e) {
+ if (e instanceof SettingsUpdatedEvent) {
+ SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
+ if (s.getNamespace().equals(getId().getString()))
+ ioExecutor.execute(() -> onSettingsUpdated(s.getSettings()));
+ }
+ }
+
+ @IoExecutor
+ private void onSettingsUpdated(Settings settings) {
+ boolean enabledByUser = settings.getBoolean(PREF_TCP_ENABLE, false);
+ ServerSocket ss = state.setEnabledByUser(enabledByUser);
+ State s = getState();
+ if (ss != null) {
+ LOG.info("Disabled by user, closing server socket");
+ tryToClose(ss, LOG, WARNING);
+ } else if (s == INACTIVE) {
+ LOG.info("Enabled by user, opening server socket");
+ bind();
+ }
+ }
+
@ThreadSafe
@NotNullByDefault
protected class PluginState {
@GuardedBy("this")
- private boolean started = false, stopped = false;
+ private boolean started = false, stopped = false, enabledByUser = false;
+
@GuardedBy("this")
@Nullable
private ServerSocket serverSocket = null;
- synchronized void setStarted() {
+ synchronized void setStarted(boolean enabledByUser) {
started = true;
+ this.enabledByUser = enabledByUser;
callback.pluginStateChanged(getState());
}
@@ -355,6 +387,18 @@ abstract class TcpPlugin implements DuplexPlugin {
return ss;
}
+ @Nullable
+ synchronized ServerSocket setEnabledByUser(boolean enabledByUser) {
+ this.enabledByUser = enabledByUser;
+ ServerSocket ss = null;
+ if (!enabledByUser) {
+ ss = serverSocket;
+ serverSocket = null;
+ }
+ callback.pluginStateChanged(getState());
+ return ss;
+ }
+
@Nullable
synchronized ServerSocket getServerSocket() {
return serverSocket;
@@ -373,8 +417,13 @@ abstract class TcpPlugin implements DuplexPlugin {
}
synchronized State getState() {
- if (!started || stopped) return DISABLED;
+ if (!started || stopped || !enabledByUser) return DISABLED;
return serverSocket == null ? INACTIVE : ACTIVE;
}
+
+ synchronized int getReasonDisabled() {
+ if (!started || stopped) return REASON_STARTING_STOPPING;
+ return enabledByUser ? -1 : REASON_USER;
+ }
}
}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/WanTcpPluginFactory.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/WanTcpPluginFactory.java
index 440b208f1..7237f71e1 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/WanTcpPluginFactory.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/WanTcpPluginFactory.java
@@ -1,5 +1,6 @@
package org.briarproject.bramble.plugin.tcp;
+import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
@@ -26,12 +27,14 @@ public class WanTcpPluginFactory implements DuplexPluginFactory {
private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor;
+ private final EventBus eventBus;
private final BackoffFactory backoffFactory;
private final ShutdownManager shutdownManager;
- public WanTcpPluginFactory(Executor ioExecutor,
+ public WanTcpPluginFactory(Executor ioExecutor, EventBus eventBus,
BackoffFactory backoffFactory, ShutdownManager shutdownManager) {
this.ioExecutor = ioExecutor;
+ this.eventBus = eventBus;
this.backoffFactory = backoffFactory;
this.shutdownManager = shutdownManager;
}
@@ -50,8 +53,10 @@ public class WanTcpPluginFactory implements DuplexPluginFactory {
public DuplexPlugin createPlugin(PluginCallback callback) {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
- return new WanTcpPlugin(ioExecutor, backoff,
+ WanTcpPlugin plugin = new WanTcpPlugin(ioExecutor, backoff,
new PortMapperImpl(shutdownManager), callback, MAX_LATENCY,
MAX_IDLE_TIME);
+ eventBus.addListener(plugin);
+ return plugin;
}
}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java
index d20901002..afdeb6dd6 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java
@@ -75,6 +75,7 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING;
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
import static org.briarproject.bramble.api.plugin.TorConstants.CONTROL_PORT;
import static org.briarproject.bramble.api.plugin.TorConstants.ID;
+import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ENABLE;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_AUTOMATIC;
@@ -87,7 +88,6 @@ import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V3;
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY;
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED;
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA;
-import static org.briarproject.bramble.api.plugin.TorConstants.REASON_USER;
import static org.briarproject.bramble.plugin.tor.TorRendezvousCrypto.SEED_BYTES;
import static org.briarproject.bramble.util.IoUtils.copyAndClose;
import static org.briarproject.bramble.util.IoUtils.tryToClose;
@@ -200,7 +200,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
}
}
// Load the settings
- settings = callback.getSettings();
+ settings = migrateSettings(callback.getSettings());
// Install or update the assets if necessary
if (!assetsAreUpToDate()) installAssets();
if (cookieFile.exists() && !cookieFile.delete())
@@ -288,6 +288,18 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
bind();
}
+ // TODO: Remove after a reasonable migration period (added 2020-01-16)
+ private Settings migrateSettings(Settings settings) {
+ int network = settings.getInt(PREF_TOR_NETWORK,
+ PREF_TOR_NETWORK_AUTOMATIC);
+ if (network == PREF_TOR_NETWORK_NEVER) {
+ settings.putInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_AUTOMATIC);
+ settings.putBoolean(PREF_TOR_ENABLE, false);
+ callback.mergeSettings(settings);
+ }
+ return settings;
+ }
+
private boolean assetsAreUpToDate() {
return doneFile.lastModified() > getLastUpdateTime();
}
@@ -763,6 +775,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
String country = locationUtils.getCurrentCountry();
boolean blocked =
circumventionProvider.isTorProbablyBlocked(country);
+ boolean enabledByUser = settings.getBoolean(PREF_TOR_ENABLE, true);
int network = settings.getInt(PREF_TOR_NETWORK,
PREF_TOR_NETWORK_AUTOMATIC);
boolean useMobile = settings.getBoolean(PREF_TOR_MOBILE, true);
@@ -785,7 +798,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (!online) {
LOG.info("Disabling network, device is offline");
- } else if (network == PREF_TOR_NETWORK_NEVER) {
+ } else if (!enabledByUser) {
LOG.info("Disabling network, user has disabled Tor");
disabledBySettings = true;
reasonDisabled = REASON_USER;
@@ -870,11 +883,11 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
callback.pluginStateChanged(getState());
}
+ // Doesn't affect getState()
synchronized void setTorStarted() {
torStarted = true;
}
- @SuppressWarnings("BooleanMethodIsAlwaysInverted")
synchronized boolean isTorRunning() {
return torStarted && !stopped;
}
@@ -914,12 +927,14 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
callback.pluginStateChanged(getState());
}
+ // Doesn't affect getState()
synchronized boolean setServerSocket(ServerSocket ss) {
if (stopped || serverSocket != null) return false;
serverSocket = ss;
return true;
}
+ // Doesn't affect getState()
synchronized void clearServerSocket(ServerSocket ss) {
if (serverSocket == ss) serverSocket = null;
}
diff --git a/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 7b175f805..af5f23da9 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
@@ -34,6 +34,7 @@ import static java.util.concurrent.Executors.newCachedThreadPool;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN;
+import static org.briarproject.bramble.api.plugin.TcpConstants.PREF_TCP_ENABLE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -345,10 +346,15 @@ public class LanTcpPluginTest extends BrambleTestCase {
private final CountDownLatch propertiesLatch = new CountDownLatch(1);
private final CountDownLatch connectionsLatch = new CountDownLatch(1);
private final TransportProperties local = new TransportProperties();
+ private final Settings settings = new Settings();
+
+ private Callback() {
+ settings.putBoolean(PREF_TCP_ENABLE, true);
+ }
@Override
public Settings getSettings() {
- return new Settings();
+ return settings;
}
@Override
diff --git a/bramble-java/src/main/java/org/briarproject/bramble/plugin/DesktopPluginModule.java b/bramble-java/src/main/java/org/briarproject/bramble/plugin/DesktopPluginModule.java
index 5b4cf7014..ce7ceb48b 100644
--- a/bramble-java/src/main/java/org/briarproject/bramble/plugin/DesktopPluginModule.java
+++ b/bramble-java/src/main/java/org/briarproject/bramble/plugin/DesktopPluginModule.java
@@ -37,9 +37,9 @@ public class DesktopPluginModule extends PluginModule {
backoffFactory);
DuplexPluginFactory modem = new ModemPluginFactory(ioExecutor,
reliabilityFactory);
- DuplexPluginFactory lan = new LanTcpPluginFactory(ioExecutor,
+ DuplexPluginFactory lan = new LanTcpPluginFactory(ioExecutor, eventBus,
backoffFactory);
- DuplexPluginFactory wan = new WanTcpPluginFactory(ioExecutor,
+ DuplexPluginFactory wan = new WanTcpPluginFactory(ioExecutor, eventBus,
backoffFactory, shutdownManager);
Collection duplex =
asList(bluetooth, modem, lan, wan);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeActivity.java
index 062f12059..f18c41f75 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeActivity.java
@@ -9,6 +9,7 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
+import org.briarproject.briar.android.fragment.BaseFragment;
import javax.annotation.Nullable;
import javax.inject.Inject;
@@ -79,13 +80,13 @@ public class ContactExchangeActivity extends KeyAgreementActivity {
@UiThread
private void contactExchangeFailed() {
- showErrorFragment(R.string.connection_error_explanation);
+ showErrorFragment();
}
@UiThread
@Override
public void keyAgreementFailed() {
- showErrorFragment(R.string.connection_error_explanation);
+ showErrorFragment();
}
@UiThread
@@ -103,7 +104,7 @@ public class ContactExchangeActivity extends KeyAgreementActivity {
@UiThread
@Override
public void keyAgreementAborted(boolean remoteAborted) {
- showErrorFragment(R.string.connection_error_explanation);
+ showErrorFragment();
}
@UiThread
@@ -112,4 +113,10 @@ public class ContactExchangeActivity extends KeyAgreementActivity {
startContactExchange(result);
return getString(R.string.exchanging_contact_details);
}
+
+ protected void showErrorFragment() {
+ String errorMsg = getString(R.string.connection_error_explanation);
+ BaseFragment f = ContactExchangeErrorFragment.newInstance(errorMsg);
+ showNextFragment(f);
+ }
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java
index 16d8e2a03..771c17cdc 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java
@@ -8,10 +8,23 @@ import android.content.IntentFilter;
import android.os.Bundle;
import android.view.MenuItem;
+import org.briarproject.bramble.api.db.DbException;
+import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus;
+import org.briarproject.bramble.api.event.EventListener;
+import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
+import org.briarproject.bramble.api.plugin.BluetoothConstants;
+import org.briarproject.bramble.api.plugin.LanTcpConstants;
+import org.briarproject.bramble.api.plugin.Plugin;
+import org.briarproject.bramble.api.plugin.Plugin.State;
+import org.briarproject.bramble.api.plugin.PluginManager;
+import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.event.BluetoothEnabledEvent;
+import org.briarproject.bramble.api.plugin.event.TransportStateEvent;
+import org.briarproject.bramble.api.settings.Settings;
+import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
@@ -21,6 +34,7 @@ import org.briarproject.briar.android.keyagreement.IntroFragment.IntroScreenSeen
import org.briarproject.briar.android.keyagreement.KeyAgreementFragment.KeyAgreementEventListener;
import org.briarproject.briar.android.util.UiUtils;
+import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.Nullable;
@@ -37,13 +51,18 @@ import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.CAMERA;
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
-import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
-import static android.bluetooth.BluetoothAdapter.EXTRA_SCAN_MODE;
-import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
-import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
-import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static java.util.logging.Level.INFO;
+import static java.util.logging.Level.WARNING;
+import static java.util.logging.Logger.getLogger;
+import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
+import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENABLE;
+import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
+import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED;
+import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
+import static org.briarproject.bramble.api.plugin.TcpConstants.PREF_TCP_ENABLE;
+import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_BLUETOOTH_DISCOVERABLE;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA_LOCATION;
@@ -51,10 +70,33 @@ import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMI
@ParametersNotNullByDefault
public abstract class KeyAgreementActivity extends BriarActivity implements
BaseFragmentListener, IntroScreenSeenListener,
- KeyAgreementEventListener {
+ KeyAgreementEventListener, EventListener {
- private enum BluetoothState {
- UNKNOWN, NO_ADAPTER, WAITING, REFUSED, ENABLED, DISCOVERABLE
+ private enum BluetoothDecision {
+ /**
+ * We haven't asked the user about Bluetooth discoverability.
+ */
+ UNKNOWN,
+
+ /**
+ * The device doesn't have a Bluetooth adapter.
+ */
+ NO_ADAPTER,
+
+ /**
+ * We're waiting for the user to accept or refuse discoverability.
+ */
+ WAITING,
+
+ /**
+ * The user has accepted discoverability.
+ */
+ ACCEPTED,
+
+ /**
+ * The user has refused discoverability.
+ */
+ REFUSED
}
private enum Permission {
@@ -62,11 +104,21 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
}
private static final Logger LOG =
- Logger.getLogger(KeyAgreementActivity.class.getName());
+ getLogger(KeyAgreementActivity.class.getName());
@Inject
EventBus eventBus;
+ @Inject
+ PluginManager pluginManager;
+
+ @Inject
+ @IoExecutor
+ Executor ioExecutor;
+
+ @Inject
+ SettingsManager settingsManager;
+
/**
* Set to true in onPostResume() and false in onPause(). This prevents the
* QR code fragment from being shown if onRequestPermissionsResult() is
@@ -74,21 +126,36 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
* https://issuetracker.google.com/issues/37067655.
*/
private boolean isResumed = false;
+
/**
* Set to true when the continue button is clicked, and false when the QR
* code fragment is shown. This prevents the QR code fragment from being
* shown automatically before the continue button has been clicked.
*/
private boolean continueClicked = false;
+
/**
* Records whether the Bluetooth adapter was already enabled before we
* asked for Bluetooth discoverability, so we know whether to broadcast a
* {@link BluetoothEnabledEvent}.
*/
private boolean wasAdapterEnabled = false;
+
+ /**
+ * Records whether we've enabled the wifi plugin so we don't enable it more
+ * than once.
+ */
+ private boolean hasEnabledWifi = false;
+
+ /**
+ * Records whether we've enabled the Bluetooth plugin so we don't enable it
+ * more than once.
+ */
+ private boolean hasEnabledBluetooth = false;
+
private Permission cameraPermission = Permission.UNKNOWN;
private Permission locationPermission = Permission.UNKNOWN;
- private BluetoothState bluetoothState = BluetoothState.UNKNOWN;
+ private BluetoothDecision bluetoothDecision = BluetoothDecision.UNKNOWN;
private BroadcastReceiver bluetoothReceiver = null;
@Override
@@ -96,20 +163,17 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
component.inject(this);
}
- @SuppressWarnings("ConstantConditions")
@Override
public void onCreate(@Nullable Bundle state) {
super.onCreate(state);
setContentView(R.layout.activity_fragment_container_toolbar);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
if (state == null) {
showInitialFragment(IntroFragment.newInstance());
}
- IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_STATE_CHANGED);
- filter.addAction(ACTION_SCAN_MODE_CHANGED);
+ IntentFilter filter = new IntentFilter(ACTION_SCAN_MODE_CHANGED);
bluetoothReceiver = new BluetoothStateReceiver();
registerReceiver(bluetoothReceiver, filter);
}
@@ -122,18 +186,17 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
}
+ return super.onOptionsItemSelected(item);
}
@Override
public void onStart() {
super.onStart();
+ eventBus.addListener(this);
// Permissions may have been granted manually while we were stopped
cameraPermission = Permission.UNKNOWN;
locationPermission = Permission.UNKNOWN;
@@ -150,11 +213,22 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
private void showQrCodeFragmentIfAllowed() {
if (isResumed && continueClicked && areEssentialPermissionsGranted()) {
- if (bluetoothState == BluetoothState.UNKNOWN ||
- bluetoothState == BluetoothState.ENABLED) {
- requestBluetoothDiscoverable();
- } else if (bluetoothState != BluetoothState.WAITING) {
+ if (isWifiReady() && isBluetoothReady()) {
+ LOG.info("Wifi and Bluetooth are ready");
showQrCodeFragment();
+ } else {
+ if (shouldEnableWifi()) {
+ LOG.info("Enabling wifi plugin");
+ hasEnabledWifi = true;
+ enablePlugin(LanTcpConstants.ID, PREF_TCP_ENABLE);
+ }
+ if (bluetoothDecision == BluetoothDecision.UNKNOWN) {
+ requestBluetoothDiscoverable();
+ } else if (shouldEnableBluetooth()) {
+ LOG.info("Enabling Bluetooth plugin");
+ hasEnabledBluetooth = true;
+ enablePlugin(BluetoothConstants.ID, PREF_BT_ENABLE);
+ }
}
}
}
@@ -167,57 +241,116 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
locationPermission == Permission.PERMANENTLY_DENIED);
}
+ private boolean isWifiReady() {
+ Plugin p = pluginManager.getPlugin(LanTcpConstants.ID);
+ if (p == null) return true; // Continue without wifi
+ State state = p.getState();
+ // Wait for plugin to become enabled
+ return state == ACTIVE || state == INACTIVE;
+ }
+
+ private boolean isBluetoothReady() {
+ if (bluetoothDecision == BluetoothDecision.UNKNOWN ||
+ bluetoothDecision == BluetoothDecision.WAITING) {
+ // Wait for decision
+ return false;
+ }
+ if (bluetoothDecision == BluetoothDecision.NO_ADAPTER
+ || bluetoothDecision == BluetoothDecision.REFUSED) {
+ // Continue without Bluetooth
+ return true;
+ }
+ BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
+ if (bt == null) return true; // Continue without Bluetooth
+ if (bt.getScanMode() != SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
+ // Wait for adapter to become discoverable
+ return false;
+ }
+ Plugin p = pluginManager.getPlugin(BluetoothConstants.ID);
+ if (p == null) return true; // Continue without Bluetooth
+ // Wait for plugin to become active
+ return p.getState() == ACTIVE;
+ }
+
+ private boolean shouldEnableWifi() {
+ if (hasEnabledWifi) return false;
+ Plugin p = pluginManager.getPlugin(LanTcpConstants.ID);
+ return p != null && p.getState() == DISABLED;
+ }
+
+ private void enablePlugin(TransportId t, String settingKey) {
+ ioExecutor.execute(() -> {
+ try {
+ Settings s = new Settings();
+ s.putBoolean(settingKey, true);
+ settingsManager.mergeSettings(s, t.getString());
+ } catch (DbException e) {
+ logException(LOG, WARNING, e);
+ }
+ });
+ }
+
+ private void requestBluetoothDiscoverable() {
+ BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
+ if (bt == null) {
+ bluetoothDecision = BluetoothDecision.NO_ADAPTER;
+ showQrCodeFragmentIfAllowed();
+ } else {
+ Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE);
+ if (i.resolveActivity(getPackageManager()) != null) {
+ LOG.info("Asking for Bluetooth discoverability");
+ bluetoothDecision = BluetoothDecision.WAITING;
+ wasAdapterEnabled = bt.isEnabled();
+ startActivityForResult(i, REQUEST_BLUETOOTH_DISCOVERABLE);
+ } else {
+ bluetoothDecision = BluetoothDecision.NO_ADAPTER;
+ showQrCodeFragmentIfAllowed();
+ }
+ }
+ }
+
+ private boolean shouldEnableBluetooth() {
+ if (bluetoothDecision != BluetoothDecision.ACCEPTED) return false;
+ if (hasEnabledBluetooth) return false;
+ Plugin p = pluginManager.getPlugin(BluetoothConstants.ID);
+ return p != null && p.getState() == DISABLED;
+ }
+
@Override
protected void onPause() {
super.onPause();
isResumed = false;
}
+ @Override
+ protected void onStop() {
+ super.onStop();
+ eventBus.removeListener(this);
+ }
+
@Override
public void showNextScreen() {
continueClicked = true;
if (checkPermissions()) showQrCodeFragmentIfAllowed();
}
- private void requestBluetoothDiscoverable() {
- BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
- if (bt == null) {
- setBluetoothState(BluetoothState.NO_ADAPTER);
- } else {
- Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE);
- if (i.resolveActivity(getPackageManager()) != null) {
- setBluetoothState(BluetoothState.WAITING);
- wasAdapterEnabled = bt.isEnabled();
- startActivityForResult(i, REQUEST_BLUETOOTH_DISCOVERABLE);
- } else {
- setBluetoothState(BluetoothState.NO_ADAPTER);
- }
- }
- }
-
- private void setBluetoothState(BluetoothState bluetoothState) {
- LOG.info("Setting Bluetooth state to " + bluetoothState);
- this.bluetoothState = bluetoothState;
- if (!wasAdapterEnabled && bluetoothState == BluetoothState.ENABLED) {
- eventBus.broadcast(new BluetoothEnabledEvent());
- wasAdapterEnabled = true;
- }
- showQrCodeFragmentIfAllowed();
- }
-
@Override
- public void onActivityResult(int request, int result, Intent data) {
+ public void onActivityResult(int request, int result,
+ @Nullable Intent data) {
if (request == REQUEST_BLUETOOTH_DISCOVERABLE) {
if (result == RESULT_CANCELED) {
- setBluetoothState(BluetoothState.REFUSED);
+ LOG.info("Bluetooth discoverability was refused");
+ bluetoothDecision = BluetoothDecision.REFUSED;
} else {
- // If Bluetooth is already discoverable, show the QR code -
- // otherwise wait for the state or scan mode to change
- BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
- if (bt == null) throw new AssertionError();
- if (bt.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE)
- setBluetoothState(BluetoothState.DISCOVERABLE);
+ LOG.info("Bluetooth discoverability was accepted");
+ bluetoothDecision = BluetoothDecision.ACCEPTED;
+ if (!wasAdapterEnabled) {
+ LOG.info("Bluetooth adapter was enabled by us");
+ eventBus.broadcast(new BluetoothEnabledEvent());
+ wasAdapterEnabled = true;
+ }
}
+ showQrCodeFragmentIfAllowed();
} else super.onActivityResult(request, result, data);
}
@@ -227,7 +360,12 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
continueClicked = false;
// If we return to the intro fragment, ask for Bluetooth
// discoverability again before showing the QR code fragment
- bluetoothState = BluetoothState.UNKNOWN;
+ bluetoothDecision = BluetoothDecision.UNKNOWN;
+ // If we return to the intro fragment, we may need to enable wifi and
+ // Bluetooth again
+ hasEnabledWifi = false;
+ hasEnabledBluetooth = false;
+
// FIXME #824
FragmentManager fm = getSupportFragmentManager();
if (fm.findFragmentByTag(KeyAgreementFragment.TAG) == null) {
@@ -239,12 +377,6 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
}
}
- protected void showErrorFragment(@StringRes int errorResId) {
- String errorMsg = getString(errorResId);
- BaseFragment f = ContactExchangeErrorFragment.newInstance(errorMsg);
- showNextFragment(f);
- }
-
private boolean checkPermissions() {
if (areEssentialPermissionsGranted()) return true;
// If the camera permission has been permanently denied, ask the
@@ -335,24 +467,30 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
permission);
}
+ @Override
+ public void eventOccurred(Event e) {
+ if (e instanceof TransportStateEvent) {
+ TransportStateEvent t = (TransportStateEvent) e;
+ if (t.getTransportId().equals(BluetoothConstants.ID)) {
+ if (LOG.isLoggable(INFO)) {
+ LOG.info("Bluetooth state changed to " + t.getState());
+ }
+ showQrCodeFragmentIfAllowed();
+ } else if (t.getTransportId().equals(LanTcpConstants.ID)) {
+ if (LOG.isLoggable(INFO)) {
+ LOG.info("Wifi state changed to " + t.getState());
+ }
+ showQrCodeFragmentIfAllowed();
+ }
+ }
+ }
+
private class BluetoothStateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (ACTION_STATE_CHANGED.equals(action)) {
- int state = intent.getIntExtra(EXTRA_STATE, 0);
- if (state == STATE_ON)
- setBluetoothState(BluetoothState.ENABLED);
- else setBluetoothState(BluetoothState.UNKNOWN);
- } else if (ACTION_SCAN_MODE_CHANGED.equals(action)) {
- int scanMode = intent.getIntExtra(EXTRA_SCAN_MODE, 0);
- if (scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE)
- setBluetoothState(BluetoothState.DISCOVERABLE);
- else if (scanMode == SCAN_MODE_CONNECTABLE)
- setBluetoothState(BluetoothState.ENABLED);
- else setBluetoothState(BluetoothState.UNKNOWN);
- }
+ LOG.info("Bluetooth scan mode changed");
+ showQrCodeFragmentIfAllowed();
}
}
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java
index dc7096db0..4e2ab8f83 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java
@@ -21,6 +21,7 @@ import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.BluetoothConstants;
+import org.briarproject.bramble.api.plugin.LanTcpConstants;
import org.briarproject.bramble.api.plugin.TorConstants;
import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.SettingsManager;
@@ -73,6 +74,8 @@ import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_LTR;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENABLE;
+import static org.briarproject.bramble.api.plugin.TcpConstants.PREF_TCP_ENABLE;
+import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ENABLE;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_AUTOMATIC;
@@ -105,16 +108,24 @@ public class SettingsFragment extends PreferenceFragmentCompat
implements EventListener, OnPreferenceChangeListener {
public static final String SETTINGS_NAMESPACE = "android-ui";
- public static final String BT_NAMESPACE = BluetoothConstants.ID.getString();
- public static final String TOR_NAMESPACE = TorConstants.ID.getString();
public static final String LANGUAGE = "pref_key_language";
public static final String PREF_SCREEN_LOCK = "pref_key_lock";
public static final String PREF_SCREEN_LOCK_TIMEOUT =
"pref_key_lock_timeout";
public static final String NOTIFY_SIGN_IN = "pref_key_notify_sign_in";
- public static final String TOR_NETWORK = "pref_key_tor_network";
- public static final String TOR_MOBILE = "pref_key_tor_mobile_data";
- public static final String TOR_ONLY_WHEN_CHARGING =
+
+ private static final String BT_NAMESPACE =
+ BluetoothConstants.ID.getString();
+ private static final String BT_ENABLE = "pref_key_bluetooth";
+
+ private static final String WIFI_NAMESPACE = LanTcpConstants.ID.getString();
+ private static final String WIFI_ENABLE = "pref_key_wifi";
+
+ private static final String TOR_NAMESPACE = TorConstants.ID.getString();
+ private static final String TOR_ENABLE = "pref_key_tor_enable";
+ private static final String TOR_NETWORK = "pref_key_tor_network";
+ private static final String TOR_MOBILE = "pref_key_tor_mobile_data";
+ private static final String TOR_ONLY_WHEN_CHARGING =
"pref_key_tor_only_when_charging";
private static final Logger LOG =
@@ -122,7 +133,9 @@ public class SettingsFragment extends PreferenceFragmentCompat
private SettingsActivity listener;
private ListPreference language;
- private ListPreference enableBluetooth;
+ private SwitchPreference enableBluetooth;
+ private SwitchPreference enableWifi;
+ private SwitchPreference enableTor;
private ListPreference torNetwork;
private SwitchPreference torMobile;
private SwitchPreference torOnlyWhenCharging;
@@ -137,7 +150,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
private Preference notifySound;
// Fields that are accessed from background threads must be volatile
- private volatile Settings settings, btSettings, torSettings;
+ private volatile Settings settings, btSettings, wifiSettings, torSettings;
private volatile boolean settingsLoaded = false;
@Inject
@@ -163,28 +176,23 @@ public class SettingsFragment extends PreferenceFragmentCompat
public void onCreatePreferences(Bundle bundle, String s) {
addPreferencesFromResource(R.xml.settings);
- language = (ListPreference) findPreference(LANGUAGE);
+ language = findPreference(LANGUAGE);
setLanguageEntries();
- ListPreference theme =
- (ListPreference) findPreference("pref_key_theme");
- enableBluetooth = (ListPreference) findPreference("pref_key_bluetooth");
- torNetwork = (ListPreference) findPreference(TOR_NETWORK);
- torMobile = (SwitchPreference) findPreference(TOR_MOBILE);
- torOnlyWhenCharging =
- (SwitchPreference) findPreference(TOR_ONLY_WHEN_CHARGING);
- screenLock = (SwitchPreference) findPreference(PREF_SCREEN_LOCK);
- screenLockTimeout =
- (ListPreference) findPreference(PREF_SCREEN_LOCK_TIMEOUT);
- notifyPrivateMessages = (SwitchPreference) findPreference(
- "pref_key_notify_private_messages");
- notifyGroupMessages = (SwitchPreference) findPreference(
- "pref_key_notify_group_messages");
- notifyForumPosts = (SwitchPreference) findPreference(
- "pref_key_notify_forum_posts");
- notifyBlogPosts = (SwitchPreference) findPreference(
- "pref_key_notify_blog_posts");
- notifyVibration = (SwitchPreference) findPreference(
- "pref_key_notify_vibration");
+ ListPreference theme = findPreference("pref_key_theme");
+ enableBluetooth = findPreference(BT_ENABLE);
+ enableWifi = findPreference(WIFI_ENABLE);
+ enableTor = findPreference(TOR_ENABLE);
+ torNetwork = findPreference(TOR_NETWORK);
+ torMobile = findPreference(TOR_MOBILE);
+ torOnlyWhenCharging = findPreference(TOR_ONLY_WHEN_CHARGING);
+ screenLock = findPreference(PREF_SCREEN_LOCK);
+ screenLockTimeout = findPreference(PREF_SCREEN_LOCK_TIMEOUT);
+ notifyPrivateMessages =
+ findPreference("pref_key_notify_private_messages");
+ notifyGroupMessages = findPreference("pref_key_notify_group_messages");
+ notifyForumPosts = findPreference("pref_key_notify_forum_posts");
+ notifyBlogPosts = findPreference("pref_key_notify_blog_posts");
+ notifyVibration = findPreference("pref_key_notify_vibration");
notifySound = findPreference("pref_key_notify_sound");
language.setOnPreferenceChangeListener(this);
@@ -194,8 +202,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
UiUtils.setTheme(getActivity(), (String) newValue);
// bring up parent activity, so it can change its theme as well
// upstream bug: https://issuetracker.google.com/issues/38352704
- Intent intent =
- new Intent(getActivity(), ENTRY_ACTIVITY);
+ Intent intent = new Intent(getActivity(), ENTRY_ACTIVITY);
intent.setFlags(
FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
@@ -207,6 +214,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
return true;
});
enableBluetooth.setOnPreferenceChangeListener(this);
+ enableWifi.setOnPreferenceChangeListener(this);
+ enableTor.setOnPreferenceChangeListener(this);
torNetwork.setOnPreferenceChangeListener(this);
torMobile.setOnPreferenceChangeListener(this);
torOnlyWhenCharging.setOnPreferenceChangeListener(this);
@@ -335,7 +344,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
boolean blocked =
circumventionProvider.isTorProbablyBlocked(country);
boolean useBridges = circumventionProvider.doBridgesWork(country);
- String setting = getString(R.string.tor_network_setting_without_bridges);
+ String setting =
+ getString(R.string.tor_network_setting_without_bridges);
if (blocked && useBridges) {
setting = getString(R.string.tor_network_setting_with_bridges);
} else if (blocked) {
@@ -352,6 +362,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
long start = now();
settings = settingsManager.getSettings(SETTINGS_NAMESPACE);
btSettings = settingsManager.getSettings(BT_NAMESPACE);
+ wifiSettings = settingsManager.getSettings(WIFI_NAMESPACE);
torSettings = settingsManager.getSettings(TOR_NAMESPACE);
settingsLoaded = true;
logDuration(LOG, "Loading settings", start);
@@ -369,7 +380,15 @@ public class SettingsFragment extends PreferenceFragmentCompat
boolean btEnabledSetting =
btSettings.getBoolean(PREF_BT_ENABLE, false);
- enableBluetooth.setValue(Boolean.toString(btEnabledSetting));
+ enableBluetooth.setChecked(btEnabledSetting);
+
+ boolean wifiEnabledSetting =
+ wifiSettings.getBoolean(PREF_TCP_ENABLE, false);
+ enableWifi.setChecked(wifiEnabledSetting);
+
+ boolean torEnabledSetting =
+ torSettings.getBoolean(PREF_TOR_ENABLE, true);
+ enableTor.setChecked(torEnabledSetting);
int torNetworkSetting = torSettings.getInt(PREF_TOR_NETWORK,
PREF_TOR_NETWORK_AUTOMATIC);
@@ -443,6 +462,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
// - pref_key_lock (screenLock -> displayScreenLockSetting())
// - pref_key_lock_timeout (screenLockTimeout)
enableBluetooth.setEnabled(enabled);
+ enableWifi.setEnabled(enabled);
+ enableTor.setEnabled(enabled);
torNetwork.setEnabled(enabled);
torMobile.setEnabled(enabled);
torOnlyWhenCharging.setEnabled(enabled);
@@ -545,8 +566,14 @@ public class SettingsFragment extends PreferenceFragmentCompat
languageChanged((String) newValue);
return false;
} else if (preference == enableBluetooth) {
- boolean btSetting = Boolean.valueOf((String) newValue);
- storeBluetoothSettings(btSetting);
+ boolean btSetting = (Boolean) newValue;
+ storeBluetoothSetting(btSetting);
+ } else if (preference == enableWifi) {
+ boolean wifiSetting = (Boolean) newValue;
+ storeWifiSetting(wifiSetting);
+ } else if (preference == enableTor) {
+ boolean torEnabledSetting = (Boolean) newValue;
+ storeTorEnabledSetting(torEnabledSetting);
} else if (preference == torNetwork) {
int torNetworkSetting = Integer.valueOf((String) newValue);
storeTorNetworkSetting(torNetworkSetting);
@@ -610,6 +637,12 @@ public class SettingsFragment extends PreferenceFragmentCompat
builder.show();
}
+ private void storeTorEnabledSetting(boolean torEnabledSetting) {
+ Settings s = new Settings();
+ s.putBoolean(PREF_TOR_ENABLE, torEnabledSetting);
+ mergeSettings(s, TOR_NAMESPACE);
+ }
+
private void storeTorNetworkSetting(int torNetworkSetting) {
Settings s = new Settings();
s.putInt(PREF_TOR_NETWORK, torNetworkSetting);
@@ -628,12 +661,18 @@ public class SettingsFragment extends PreferenceFragmentCompat
mergeSettings(s, TOR_NAMESPACE);
}
- private void storeBluetoothSettings(boolean btSetting) {
+ private void storeBluetoothSetting(boolean btSetting) {
Settings s = new Settings();
s.putBoolean(PREF_BT_ENABLE, btSetting);
mergeSettings(s, BT_NAMESPACE);
}
+ private void storeWifiSetting(boolean wifiSetting) {
+ Settings s = new Settings();
+ s.putBoolean(PREF_TCP_ENABLE, wifiSetting);
+ mergeSettings(s, WIFI_NAMESPACE);
+ }
+
private void storeSettings(Settings s) {
mergeSettings(s, SETTINGS_NAMESPACE);
}
@@ -696,6 +735,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
LOG.info("Bluetooth settings updated");
btSettings = s.getSettings();
displaySettings();
+ } else if (namespace.equals(WIFI_NAMESPACE)) {
+ LOG.info("Wifi settings updated");
+ wifiSettings = s.getSettings();
+ displaySettings();
} else if (namespace.equals(TOR_NAMESPACE)) {
LOG.info("Tor settings updated");
torSettings = s.getSettings();
diff --git a/briar-android/src/main/res/values/arrays.xml b/briar-android/src/main/res/values/arrays.xml
index a7a950e78..a9dc6aed6 100644
--- a/briar-android/src/main/res/values/arrays.xml
+++ b/briar-android/src/main/res/values/arrays.xml
@@ -1,14 +1,5 @@
-
- - true
- - false
-
-
- - @string/bluetooth_setting_enabled
- - @string/bluetooth_setting_disabled
-
-
- @string/tor_network_setting_automatic
- @string/tor_network_setting_without_bridges
@@ -66,6 +57,7 @@
- zh-CN
- zh-TW
+
- @string/pref_theme_light
- @string/pref_theme_dark
diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml
index d7e5d7090..902a56c89 100644
--- a/briar-android/src/main/res/values/strings.xml
+++ b/briar-android/src/main/res/values/strings.xml
@@ -441,12 +441,12 @@
Automatic (Daytime)
System Default
-
- Networks
- Connect via Bluetooth
- Whenever contacts are nearby
- Only when adding contacts
- Connect via Internet (Tor)
+
+ Connections
+ Connect to contacts via Bluetooth
+ Connect to contacts on the same Wi-Fi network
+ Connect to contacts via Internet (Tor)
+ Connection method for Internet (Tor)
Automatic based on location
Use Tor without bridges
Use Tor with bridges
diff --git a/briar-android/src/main/res/xml/settings.xml b/briar-android/src/main/res/xml/settings.xml
index d45a80f86..db50e4732 100644
--- a/briar-android/src/main/res/xml/settings.xml
+++ b/briar-android/src/main/res/xml/settings.xml
@@ -29,18 +29,33 @@
android:layout="@layout/preferences_category"
android:title="@string/network_settings_title">
-
+
+
+
+