From 63d849b6847c3f8c2e1a0e029726a36b98584943 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 22 Apr 2016 10:40:24 +0100 Subject: [PATCH 1/4] Hold partial wake lock to keep Tor connections alive. --- briar-android/AndroidManifest.xml | 1 + .../briarproject/plugins/tor/TorPlugin.java | 32 +++++++++++++------ 2 files changed, 24 insertions(+), 9 deletions(-) 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..63ccc6ba5 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; @@ -57,9 +58,11 @@ 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; @@ -94,6 +97,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, private final File cookieFile, hostnameFile; private final AtomicBoolean circuitBuilt; private final AtomicInteger descriptorsPublished; + private final PowerManager.WakeLock wakeLock; private volatile boolean running = false, networkEnabled = false; private volatile boolean bootstrapped = false; @@ -132,6 +136,10 @@ class TorPlugin implements DuplexPlugin, EventHandler, 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() { @@ -488,6 +496,7 @@ 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) wakeLock.acquire(); if (!enable) { circuitBuilt.set(false); descriptorsPublished.set(0); @@ -496,6 +505,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, } networkEnabled = enable; controlConnection.setConf("DisableNetwork", enable ? "0" : "1"); + if (!enable) wakeLock.release(); } public void stop() throws IOException { @@ -517,6 +527,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, } catch (IOException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); } + wakeLock.release(); } public boolean isRunning() { @@ -709,15 +720,18 @@ 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"); + 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(); + } } } } From f58ee132449e2ab219163100e5808285fc7b6b78 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 22 Apr 2016 15:19:52 +0100 Subject: [PATCH 2/4] Reset connection status when enabling network. --- .../org/briarproject/plugins/tor/TorPlugin.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java index 63ccc6ba5..3b8e56cc6 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java @@ -495,17 +495,18 @@ class TorPlugin implements DuplexPlugin, EventHandler, private void enableNetwork(boolean enable) throws IOException { if (!running) return; + if (networkEnabled == enable) return; if (LOG.isLoggable(INFO)) LOG.info("Enabling network: " + enable); if (enable) wakeLock.acquire(); - if (!enable) { - circuitBuilt.set(false); - descriptorsPublished.set(0); - descriptorsPublishedTime = Long.MAX_VALUE; - callback.transportDisabled(); - } + circuitBuilt.set(false); + descriptorsPublished.set(0); + descriptorsPublishedTime = Long.MAX_VALUE; networkEnabled = enable; controlConnection.setConf("DisableNetwork", enable ? "0" : "1"); - if (!enable) wakeLock.release(); + if (!enable) { + callback.transportDisabled(); + wakeLock.release(); + } } public void stop() throws IOException { From a0f22ec3d1f2dc11e2129d447789f0d6f3be09f6 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 22 Apr 2016 17:55:04 +0100 Subject: [PATCH 3/4] Refactored connection status variables into inner class. --- .../briarproject/plugins/tor/TorPlugin.java | 134 ++++++++++-------- 1 file changed, 78 insertions(+), 56 deletions(-) diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java index 3b8e56cc6..3abd0feff 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java @@ -50,8 +50,6 @@ 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; @@ -60,15 +58,13 @@ 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"); @@ -93,18 +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 final PowerManager.WakeLock wakeLock; - 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 volatile boolean running = false; private volatile ServerSocket socket = null; private volatile Socket controlSocket = null; private volatile TorControlConnection controlConnection = null; @@ -127,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"); @@ -134,8 +125,6 @@ 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"); @@ -237,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(); } } @@ -495,13 +484,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, private void enableNetwork(boolean enable) throws IOException { if (!running) return; - if (networkEnabled == enable) return; - if (LOG.isLoggable(INFO)) LOG.info("Enabling network: " + enable); if (enable) wakeLock.acquire(); - circuitBuilt.set(false); - descriptorsPublished.set(0); - descriptorsPublishedTime = Long.MAX_VALUE; - networkEnabled = enable; + connectionStatus.enableNetwork(enable); controlConnection.setConf("DisableNetwork", enable ? "0" : "1"); if (!enable) { callback.transportDisabled(); @@ -532,7 +516,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, } public boolean isRunning() { - return running && networkEnabled && bootstrapped && circuitBuilt.get(); + return running && connectionStatus.isConnected(); } public boolean shouldPoll() { @@ -545,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) { @@ -616,7 +597,7 @@ 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.setCircuitBuilt()) { LOG.info("First circuit built"); if (isRunning()) callback.transportEnabled(); } @@ -638,20 +619,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 { @@ -673,7 +649,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(); } } @@ -684,17 +660,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"); @@ -702,10 +684,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) { @@ -723,16 +706,55 @@ class TorPlugin implements DuplexPlugin, EventHandler, if (!running) return; if (CONNECTIVITY_ACTION.equals(i.getAction())) { LOG.info("Detected connectivity change"); - 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(); } } } + + 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 setCircuitBuilt() { + 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; + } + } } From 400a11e3b01dc514fe2724da88a73d6a207ed5c1 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Tue, 3 May 2016 15:41:59 +0100 Subject: [PATCH 4/4] Renamed circuit status method. --- .../src/org/briarproject/plugins/tor/TorPlugin.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java index 3abd0feff..b0c651fb6 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java @@ -597,7 +597,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } public void circuitStatus(String status, String id, String path) { - if (status.equals("BUILT") && connectionStatus.setCircuitBuilt()) { + if (status.equals("BUILT") && + connectionStatus.getAndSetCircuitBuilt()) { LOG.info("First circuit built"); if (isRunning()) callback.transportEnabled(); } @@ -729,7 +730,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { bootstrapped = true; } - private synchronized boolean setCircuitBuilt() { + private synchronized boolean getAndSetCircuitBuilt() { boolean firstCircuit = !circuitBuilt; circuitBuilt = true; return firstCircuit;