diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml
index c9784c094..4ffddcb79 100644
--- a/briar-android/AndroidManifest.xml
+++ b/briar-android/AndroidManifest.xml
@@ -24,6 +24,7 @@
+
diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java
index 329985202..b0c651fb6 100644
--- a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java
+++ b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java
@@ -7,6 +7,7 @@ import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.FileObserver;
+import android.os.PowerManager;
import net.freehaven.tor.control.EventHandler;
import net.freehaven.tor.control.TorControlConnection;
@@ -49,23 +50,21 @@ import java.util.List;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.zip.ZipInputStream;
import static android.content.Context.CONNECTIVITY_SERVICE;
import static android.content.Context.MODE_PRIVATE;
+import static android.content.Context.POWER_SERVICE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
-import static android.net.ConnectivityManager.EXTRA_NO_CONNECTIVITY;
import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
-class TorPlugin implements DuplexPlugin, EventHandler,
- EventListener {
+class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
static final TransportId ID = new TransportId("tor");
@@ -90,17 +89,12 @@ class TorPlugin implements DuplexPlugin, EventHandler,
private final DuplexPluginCallback callback;
private final String architecture;
private final int maxLatency, maxIdleTime, pollingInterval, socketTimeout;
+ private final ConnectionStatus connectionStatus;
private final File torDirectory, torFile, geoIpFile, configFile, doneFile;
private final File cookieFile, hostnameFile;
- private final AtomicBoolean circuitBuilt;
- private final AtomicInteger descriptorsPublished;
-
- private volatile boolean running = false, networkEnabled = false;
- private volatile boolean bootstrapped = false;
- private volatile boolean connectedToWifi = false;
- private volatile boolean online = false;
- private volatile long descriptorsPublishedTime = Long.MAX_VALUE;
+ private final PowerManager.WakeLock wakeLock;
+ private volatile boolean running = false;
private volatile ServerSocket socket = null;
private volatile Socket controlSocket = null;
private volatile TorControlConnection controlConnection = null;
@@ -123,6 +117,7 @@ class TorPlugin implements DuplexPlugin, EventHandler,
if (maxIdleTime > Integer.MAX_VALUE / 2)
socketTimeout = Integer.MAX_VALUE;
else socketTimeout = maxIdleTime * 2;
+ connectionStatus = new ConnectionStatus(pollingInterval);
torDirectory = appContext.getDir("tor", MODE_PRIVATE);
torFile = new File(torDirectory, "tor");
geoIpFile = new File(torDirectory, "geoip");
@@ -130,8 +125,10 @@ class TorPlugin implements DuplexPlugin, EventHandler,
doneFile = new File(torDirectory, "done");
cookieFile = new File(torDirectory, ".tor/control_auth_cookie");
hostnameFile = new File(torDirectory, "hs/hostname");
- circuitBuilt = new AtomicBoolean(false);
- descriptorsPublished = new AtomicInteger(0);
+ Object o = appContext.getSystemService(POWER_SERVICE);
+ PowerManager pm = (PowerManager) o;
+ wakeLock = pm.newWakeLock(PARTIAL_WAKE_LOCK, "TorPlugin");
+ wakeLock.setReferenceCounted(false);
}
public TransportId getId() {
@@ -229,7 +226,7 @@ class TorPlugin implements DuplexPlugin, EventHandler,
String phase = controlConnection.getInfo("status/bootstrap-phase");
if (phase != null && phase.contains("PROGRESS=100")) {
LOG.info("Tor has already bootstrapped");
- bootstrapped = true;
+ connectionStatus.setBootstrapped();
sendCrashReports();
}
}
@@ -487,15 +484,13 @@ class TorPlugin implements DuplexPlugin, EventHandler,
private void enableNetwork(boolean enable) throws IOException {
if (!running) return;
- if (LOG.isLoggable(INFO)) LOG.info("Enabling network: " + enable);
- if (!enable) {
- circuitBuilt.set(false);
- descriptorsPublished.set(0);
- descriptorsPublishedTime = Long.MAX_VALUE;
- callback.transportDisabled();
- }
- networkEnabled = enable;
+ if (enable) wakeLock.acquire();
+ connectionStatus.enableNetwork(enable);
controlConnection.setConf("DisableNetwork", enable ? "0" : "1");
+ if (!enable) {
+ callback.transportDisabled();
+ wakeLock.release();
+ }
}
public void stop() throws IOException {
@@ -517,10 +512,11 @@ class TorPlugin implements DuplexPlugin, EventHandler,
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
+ wakeLock.release();
}
public boolean isRunning() {
- return running && networkEnabled && bootstrapped && circuitBuilt.get();
+ return running && connectionStatus.isConnected();
}
public boolean shouldPoll() {
@@ -533,16 +529,13 @@ class TorPlugin implements DuplexPlugin, EventHandler,
public void poll(Collection connected) {
if (!isRunning()) return;
- if (descriptorsPublished.get() >= MIN_DESCRIPTORS_PUBLISHED) {
- long now = clock.currentTimeMillis();
- if (now - descriptorsPublishedTime >= 2 * pollingInterval) {
- LOG.info("Hidden service descriptor published, not polling");
- return;
- }
+ if (connectionStatus.shouldPoll(clock.currentTimeMillis())) {
+ // TODO: Pass properties to connectAndCallBack()
+ for (ContactId c : callback.getRemoteProperties().keySet())
+ if (!connected.contains(c)) connectAndCallBack(c);
+ } else {
+ LOG.info("Hidden service descriptor published, not polling");
}
- // TODO: Pass properties to connectAndCallBack()
- for (ContactId c : callback.getRemoteProperties().keySet())
- if (!connected.contains(c)) connectAndCallBack(c);
}
private void connectAndCallBack(final ContactId c) {
@@ -604,7 +597,8 @@ class TorPlugin implements DuplexPlugin, EventHandler,
}
public void circuitStatus(String status, String id, String path) {
- if (status.equals("BUILT") && !circuitBuilt.getAndSet(true)) {
+ if (status.equals("BUILT") &&
+ connectionStatus.getAndSetCircuitBuilt()) {
LOG.info("First circuit built");
if (isRunning()) callback.transportEnabled();
}
@@ -626,20 +620,15 @@ class TorPlugin implements DuplexPlugin, EventHandler,
public void message(String severity, String msg) {
if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg);
if (severity.equals("NOTICE") && msg.startsWith("Bootstrapped 100%")) {
- bootstrapped = true;
+ connectionStatus.setBootstrapped();
sendCrashReports();
if (isRunning()) callback.transportEnabled();
}
}
public void unrecognized(String type, String msg) {
- if (type.equals("HS_DESC") && msg.startsWith("UPLOADED")) {
- int descriptors = descriptorsPublished.incrementAndGet();
- if (descriptors == MIN_DESCRIPTORS_PUBLISHED) {
- LOG.info("Hidden service descriptor published");
- descriptorsPublishedTime = clock.currentTimeMillis();
- }
- }
+ if (type.equals("HS_DESC") && msg.startsWith("UPLOADED"))
+ connectionStatus.descriptorPublished(clock.currentTimeMillis());
}
private static class WriteObserver extends FileObserver {
@@ -661,7 +650,7 @@ class TorPlugin implements DuplexPlugin, EventHandler,
public void eventOccurred(Event e) {
if (e instanceof SettingsUpdatedEvent) {
if (((SettingsUpdatedEvent) e).getNamespace().equals("tor")) {
- // Wifi setting may have been updated
+ LOG.info("Tor settings updated");
updateConnectionStatus();
}
}
@@ -672,17 +661,23 @@ class TorPlugin implements DuplexPlugin, EventHandler,
public void run() {
if (!running) return;
+ Object o = appContext.getSystemService(CONNECTIVITY_SERVICE);
+ ConnectivityManager cm = (ConnectivityManager) o;
+ NetworkInfo net = cm.getActiveNetworkInfo();
+ boolean online = net != null && net.isConnected();
+ boolean wifi = online && net.getType() == TYPE_WIFI;
String country = locationUtils.getCurrentCountry();
- if (LOG.isLoggable(INFO)) {
- LOG.info("Online: " + online);
- if ("".equals(country)) LOG.info("Country code unknown");
- else LOG.info("Country code: " + country);
- }
boolean blocked = TorNetworkMetadata.isTorProbablyBlocked(
country);
Settings s = callback.getSettings();
boolean useMobileData = s.getBoolean("torOverMobile", true);
+ if (LOG.isLoggable(INFO)) {
+ LOG.info("Online: " + online + ", wifi: " + wifi);
+ if ("".equals(country)) LOG.info("Country code unknown");
+ else LOG.info("Country code: " + country);
+ }
+
try {
if (!online) {
LOG.info("Disabling network, device is offline");
@@ -690,10 +685,11 @@ class TorPlugin implements DuplexPlugin, EventHandler,
} else if (blocked) {
LOG.info("Disabling network, country is blocked");
enableNetwork(false);
- } else if (!useMobileData & !connectedToWifi) {
+ } else if (!wifi && !useMobileData) {
LOG.info("Disabling network due to data setting");
enableNetwork(false);
} else {
+ LOG.info("Enabling network");
enableNetwork(true);
}
} catch (IOException e) {
@@ -709,15 +705,57 @@ class TorPlugin implements DuplexPlugin, EventHandler,
@Override
public void onReceive(Context ctx, Intent i) {
if (!running) return;
- online = !i.getBooleanExtra(EXTRA_NO_CONNECTIVITY, false);
- // Some devices fail to set EXTRA_NO_CONNECTIVITY, double check
- Object o = ctx.getSystemService(CONNECTIVITY_SERVICE);
- ConnectivityManager cm = (ConnectivityManager) o;
- NetworkInfo net = cm.getActiveNetworkInfo();
- if (net == null || !net.isConnected()) online = false;
- connectedToWifi = (net != null && net.getType() == TYPE_WIFI
- && net.isConnected());
- updateConnectionStatus();
+ if (CONNECTIVITY_ACTION.equals(i.getAction())) {
+ LOG.info("Detected connectivity change");
+ updateConnectionStatus();
+ }
+ }
+ }
+
+ private static class ConnectionStatus {
+
+ private final int pollingInterval;
+
+ // All of the following are locking: this
+ private boolean networkEnabled = false;
+ private boolean bootstrapped = false, circuitBuilt = false;
+ private int descriptorsPublished = 0;
+ private long descriptorsPublishedTime = Long.MAX_VALUE;
+
+ private ConnectionStatus(int pollingInterval) {
+ this.pollingInterval = pollingInterval;
+ }
+
+ private synchronized void setBootstrapped() {
+ bootstrapped = true;
+ }
+
+ private synchronized boolean getAndSetCircuitBuilt() {
+ boolean firstCircuit = !circuitBuilt;
+ circuitBuilt = true;
+ return firstCircuit;
+ }
+
+ private synchronized void descriptorPublished(long now) {
+ descriptorsPublished++;
+ if (descriptorsPublished == MIN_DESCRIPTORS_PUBLISHED)
+ descriptorsPublishedTime = now;
+ }
+
+ private synchronized void enableNetwork(boolean enable) {
+ networkEnabled = enable;
+ circuitBuilt = false;
+ descriptorsPublished = 0;
+ descriptorsPublishedTime = Long.MAX_VALUE;
+ }
+
+ private synchronized boolean isConnected() {
+ return networkEnabled && bootstrapped && circuitBuilt;
+ }
+
+ private synchronized boolean shouldPoll(long now) {
+ return descriptorsPublished < MIN_DESCRIPTORS_PUBLISHED
+ || now - descriptorsPublishedTime < 2 * pollingInterval;
}
}
}