From 7e05a49bdaa9ecc5ee6e56de90cad64daba9e5c5 Mon Sep 17 00:00:00 2001
From: Torsten Grote <t@grobox.de>
Date: Tue, 26 Jun 2018 15:47:19 -0300
Subject: [PATCH] Add Android integration tests that checks if included bridges
 work

This also changes the way bridges are used.
Instead of using the torrc config file,
bridges are now activated via Tor's control port.
---
 bramble-android/build.gradle                  |   8 ++
 .../bramble/IntegrationTestComponent.java     |  23 ++++
 .../bramble/plugin/tor/BridgeTest.java        | 102 ++++++++++++++++++
 .../bramble/plugin/tor/TorPluginCallBack.java |  54 ++++++++++
 .../bramble/BrambleAndroidModule.java         |  12 +++
 .../bramble/plugin/tor/BridgeProvider.java    |   9 ++
 .../plugin/tor/BridgeProviderImpl.java        |  20 ++++
 .../bramble/plugin/tor/TorPlugin.java         |  20 +++-
 .../bramble/plugin/tor/TorPluginFactory.java  |   5 +-
 bramble-android/src/main/res/raw/torrc        |   5 -
 .../briarproject/briar/android/AppModule.java |   5 +-
 11 files changed, 253 insertions(+), 10 deletions(-)
 create mode 100644 bramble-android/src/androidTest/java/org/briarproject/bramble/IntegrationTestComponent.java
 create mode 100644 bramble-android/src/androidTest/java/org/briarproject/bramble/plugin/tor/BridgeTest.java
 create mode 100644 bramble-android/src/androidTest/java/org/briarproject/bramble/plugin/tor/TorPluginCallBack.java
 create mode 100644 bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/BridgeProvider.java
 create mode 100644 bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/BridgeProviderImpl.java

diff --git a/bramble-android/build.gradle b/bramble-android/build.gradle
index f2f2ba5de5..fe202931dc 100644
--- a/bramble-android/build.gradle
+++ b/bramble-android/build.gradle
@@ -11,6 +11,8 @@ android {
 		versionCode 10011
 		versionName "1.0.11"
 		consumerProguardFiles 'proguard-rules.txt'
+
+		testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
 	}
 
 	compileOptions {
@@ -31,6 +33,12 @@ dependencies {
 	annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
 
 	compileOnly 'javax.annotation:jsr250-api:1.0'
+
+	androidTestImplementation project(path: ':bramble-api', configuration: 'testOutput')
+	androidTestImplementation project(path: ':bramble-core', configuration: 'testOutput')
+	androidTestImplementation 'com.android.support.test:runner:1.0.2'
+	androidTestAnnotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
+	androidTestCompileOnly 'javax.annotation:jsr250-api:1.0'
 }
 
 dependencyVerification {
diff --git a/bramble-android/src/androidTest/java/org/briarproject/bramble/IntegrationTestComponent.java b/bramble-android/src/androidTest/java/org/briarproject/bramble/IntegrationTestComponent.java
new file mode 100644
index 0000000000..bb5875ac33
--- /dev/null
+++ b/bramble-android/src/androidTest/java/org/briarproject/bramble/IntegrationTestComponent.java
@@ -0,0 +1,23 @@
+package org.briarproject.bramble;
+
+import org.briarproject.bramble.event.EventModule;
+import org.briarproject.bramble.plugin.PluginModule;
+import org.briarproject.bramble.plugin.tor.BridgeTest;
+import org.briarproject.bramble.system.SystemModule;
+
+import javax.inject.Singleton;
+
+import dagger.Component;
+
+@Singleton
+@Component(modules = {
+		BrambleAndroidModule.class,
+		PluginModule.class,  // needed for BackoffFactory
+		EventModule.class,
+		SystemModule.class,
+})
+public interface IntegrationTestComponent {
+
+	void inject(BridgeTest init);
+
+}
diff --git a/bramble-android/src/androidTest/java/org/briarproject/bramble/plugin/tor/BridgeTest.java b/bramble-android/src/androidTest/java/org/briarproject/bramble/plugin/tor/BridgeTest.java
new file mode 100644
index 0000000000..cb8c4bd3f7
--- /dev/null
+++ b/bramble-android/src/androidTest/java/org/briarproject/bramble/plugin/tor/BridgeTest.java
@@ -0,0 +1,102 @@
+package org.briarproject.bramble.plugin.tor;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.briarproject.bramble.DaggerIntegrationTestComponent;
+import org.briarproject.bramble.IntegrationTestComponent;
+import org.briarproject.bramble.api.event.EventBus;
+import org.briarproject.bramble.api.plugin.BackoffFactory;
+import org.briarproject.bramble.api.system.Clock;
+import org.briarproject.bramble.api.system.LocationUtils;
+import org.briarproject.bramble.test.BrambleTestCase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+import javax.net.SocketFactory;
+
+import static java.util.Collections.singletonList;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.briarproject.bramble.plugin.tor.BridgeProviderImpl.BRIDGES;
+import static org.briarproject.bramble.plugin.tor.TorNetworkMetadata.doBridgesWork;
+import static org.briarproject.bramble.plugin.tor.TorNetworkMetadata.isTorProbablyBlocked;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+
+@RunWith(AndroidJUnit4.class)
+public class BridgeTest extends BrambleTestCase {
+
+	private final static String BRIDGE_COUNTRY = "VE";
+	private final static long TIMEOUT = SECONDS.toMillis(23);
+
+	private final static Logger LOG =
+			Logger.getLogger(BridgeTest.class.getSimpleName());
+
+	@Inject
+	EventBus eventBus;
+	@Inject
+	BackoffFactory backoffFactory;
+	@Inject
+	Clock clock;
+
+	private final TorPluginFactory factory;
+	private TorPlugin plugin;
+	private int currentBridge = 0;
+
+	public BridgeTest() {
+		IntegrationTestComponent component =
+				DaggerIntegrationTestComponent.builder().build();
+		component.inject(this);
+
+		Executor ioExecutor = Executors.newCachedThreadPool();
+		ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
+		Context appContext = InstrumentationRegistry.getTargetContext();
+		LocationUtils locationUtils = () -> BRIDGE_COUNTRY;
+		SocketFactory torSocketFactory = SocketFactory.getDefault();
+		BridgeProvider bridgeProvider =
+				() -> singletonList(BRIDGES[currentBridge]);
+		factory = new TorPluginFactory(ioExecutor, scheduler, appContext,
+				locationUtils, eventBus, torSocketFactory,
+				backoffFactory, bridgeProvider, clock);
+	}
+
+	@Test
+	public void testBridges() throws Exception {
+		assertTrue(isTorProbablyBlocked(BRIDGE_COUNTRY));
+		assertTrue(doBridgesWork(BRIDGE_COUNTRY));
+		assertTrue(BRIDGES.length > 0);
+
+		for (int i = 0; i < BRIDGES.length; i++) {
+			plugin = (TorPlugin) factory.createPlugin(new TorPluginCallBack());
+			testBridge(i);
+		}
+	}
+
+	private void testBridge(int bridge) throws Exception {
+		currentBridge = bridge;
+		LOG.warning("Testing " + BRIDGES[currentBridge]);
+		try {
+			plugin.start();
+			long start = clock.currentTimeMillis();
+			while (clock.currentTimeMillis() - start < TIMEOUT) {
+				if (plugin.isRunning()) return;
+				clock.sleep(500);
+			}
+			if (!plugin.isRunning()) {
+				fail("Could not connect to Tor within timeout.");
+			}
+		} finally {
+			plugin.stop();
+		}
+	}
+
+}
diff --git a/bramble-android/src/androidTest/java/org/briarproject/bramble/plugin/tor/TorPluginCallBack.java b/bramble-android/src/androidTest/java/org/briarproject/bramble/plugin/tor/TorPluginCallBack.java
new file mode 100644
index 0000000000..320e8f02fa
--- /dev/null
+++ b/bramble-android/src/androidTest/java/org/briarproject/bramble/plugin/tor/TorPluginCallBack.java
@@ -0,0 +1,54 @@
+package org.briarproject.bramble.plugin.tor;
+
+import org.briarproject.bramble.api.contact.ContactId;
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
+import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
+import org.briarproject.bramble.api.properties.TransportProperties;
+import org.briarproject.bramble.api.settings.Settings;
+
+@NotNullByDefault
+public class TorPluginCallBack implements DuplexPluginCallback {
+
+	@Override
+	public void incomingConnectionCreated(DuplexTransportConnection d) {
+
+	}
+
+	@Override
+	public void outgoingConnectionCreated(ContactId c,
+			DuplexTransportConnection d) {
+
+	}
+
+	@Override
+	public Settings getSettings() {
+		return new Settings();
+	}
+
+	@Override
+	public TransportProperties getLocalProperties() {
+		return new TransportProperties();
+	}
+
+	@Override
+	public void mergeSettings(Settings s) {
+
+	}
+
+	@Override
+	public void mergeLocalProperties(TransportProperties p) {
+
+	}
+
+	@Override
+	public void transportEnabled() {
+
+	}
+
+	@Override
+	public void transportDisabled() {
+
+	}
+
+}
diff --git a/bramble-android/src/main/java/org/briarproject/bramble/BrambleAndroidModule.java b/bramble-android/src/main/java/org/briarproject/bramble/BrambleAndroidModule.java
index 1ec2563482..16f2c6c597 100644
--- a/bramble-android/src/main/java/org/briarproject/bramble/BrambleAndroidModule.java
+++ b/bramble-android/src/main/java/org/briarproject/bramble/BrambleAndroidModule.java
@@ -1,11 +1,23 @@
 package org.briarproject.bramble;
 
+import org.briarproject.bramble.plugin.tor.BridgeProvider;
+import org.briarproject.bramble.plugin.tor.BridgeProviderImpl;
 import org.briarproject.bramble.system.AndroidSystemModule;
 
+import javax.inject.Singleton;
+
 import dagger.Module;
+import dagger.Provides;
 
 @Module(includes = {
 		AndroidSystemModule.class
 })
 public class BrambleAndroidModule {
+
+	@Provides
+	@Singleton
+	BridgeProvider provideBridgeProvider() {
+		return new BridgeProviderImpl();
+	}
+
 }
diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/BridgeProvider.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/BridgeProvider.java
new file mode 100644
index 0000000000..a6a74fcb5a
--- /dev/null
+++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/BridgeProvider.java
@@ -0,0 +1,9 @@
+package org.briarproject.bramble.plugin.tor;
+
+import java.util.List;
+
+public interface BridgeProvider {
+
+	List<String> getBridges();
+
+}
diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/BridgeProviderImpl.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/BridgeProviderImpl.java
new file mode 100644
index 0000000000..c66da9c8a7
--- /dev/null
+++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/BridgeProviderImpl.java
@@ -0,0 +1,20 @@
+package org.briarproject.bramble.plugin.tor;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class BridgeProviderImpl implements BridgeProvider {
+
+	final static String[] BRIDGES = {
+		"Bridge 131.252.210.150:8081 0E858AC201BF0F3FA3C462F64844CBFFC7297A42",
+//		"Bridge 128.105.214.161:8081 1E326AAFB3FCB515015250D8FCCC8E37F91A153B",
+		"Bridge 67.205.189.122:8443 12D64D5D44E20169585E7378580C0D33A872AD98",
+		"Bridge 45.32.148.146:8443 0CE016FB2462D8BF179AE71F7D702D09DEAC3F1D",
+	};
+
+	@Override
+	public List<String> getBridges() {
+		return Arrays.asList(BRIDGES);
+	}
+
+}
diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java
index 5851c385ac..1079d7ffb3 100644
--- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java
+++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java
@@ -50,7 +50,9 @@ import java.io.OutputStream;
 import java.net.InetSocketAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -120,6 +122,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 	private final Backoff backoff;
 	private final DuplexPluginCallback callback;
 	private final String architecture;
+	private final BridgeProvider bridgeProvider;
 	private final int maxLatency, maxIdleTime, socketTimeout;
 	private final ConnectionStatus connectionStatus;
 	private final File torDirectory, torFile, geoIpFile, configFile;
@@ -139,7 +142,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 			Context appContext, LocationUtils locationUtils,
 			SocketFactory torSocketFactory, Clock clock, Backoff backoff,
 			DuplexPluginCallback callback, String architecture,
-			int maxLatency, int maxIdleTime) {
+			BridgeProvider bridgeProvider, int maxLatency, int maxIdleTime) {
 		this.ioExecutor = ioExecutor;
 		this.scheduler = scheduler;
 		this.appContext = appContext;
@@ -149,6 +152,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 		this.backoff = backoff;
 		this.callback = callback;
 		this.architecture = architecture;
+		this.bridgeProvider = bridgeProvider;
 		this.maxLatency = maxLatency;
 		this.maxIdleTime = maxIdleTime;
 		if (maxIdleTime > Integer.MAX_VALUE / 2)
@@ -499,6 +503,17 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 		}
 	}
 
+	private void enableBridges(boolean enable) throws IOException {
+		if (enable) {
+			Collection<String> conf = new ArrayList<>();
+			conf.add("UseBridges 1");
+			conf.addAll(bridgeProvider.getBridges());
+			controlConnection.setConf(conf);
+		} else {
+			controlConnection.setConf("UseBridges", "0");
+		}
+	}
+
 	@Override
 	public void stop() {
 		running = false;
@@ -681,7 +696,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 				} else if (blocked) {
 					if (doBridgesWork(country)) {
 						LOG.info("Enabling network, using bridges");
-						controlConnection.setConf("UseBridges", "1");
+						enableBridges(true);
 						enableNetwork(true);
 					} else {
 						LOG.info("Disabling network, country is blocked");
@@ -693,6 +708,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 					enableNetwork(false);
 				} else {
 					LOG.info("Enabling network");
+					enableBridges(false);
 					enableNetwork(true);
 				}
 			} catch (IOException e) {
diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPluginFactory.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPluginFactory.java
index c380cbb0c9..3472a5cd18 100644
--- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPluginFactory.java
+++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/TorPluginFactory.java
@@ -43,12 +43,14 @@ public class TorPluginFactory implements DuplexPluginFactory {
 	private final EventBus eventBus;
 	private final SocketFactory torSocketFactory;
 	private final BackoffFactory backoffFactory;
+	private final BridgeProvider bridgeProvider;
 	private final Clock clock;
 
 	public TorPluginFactory(Executor ioExecutor,
 			ScheduledExecutorService scheduler, Context appContext,
 			LocationUtils locationUtils, EventBus eventBus,
 			SocketFactory torSocketFactory, BackoffFactory backoffFactory,
+			BridgeProvider bridgeProvider,
 			Clock clock) {
 		this.ioExecutor = ioExecutor;
 		this.scheduler = scheduler;
@@ -57,6 +59,7 @@ public class TorPluginFactory implements DuplexPluginFactory {
 		this.eventBus = eventBus;
 		this.torSocketFactory = torSocketFactory;
 		this.backoffFactory = backoffFactory;
+		this.bridgeProvider = bridgeProvider;
 		this.clock = clock;
 	}
 
@@ -95,7 +98,7 @@ public class TorPluginFactory implements DuplexPluginFactory {
 				MAX_POLLING_INTERVAL, BACKOFF_BASE);
 		TorPlugin plugin = new TorPlugin(ioExecutor, scheduler, appContext,
 				locationUtils, torSocketFactory, clock, backoff, callback,
-				architecture, MAX_LATENCY, MAX_IDLE_TIME);
+				architecture, bridgeProvider, MAX_LATENCY, MAX_IDLE_TIME);
 		eventBus.addListener(plugin);
 		return plugin;
 	}
diff --git a/bramble-android/src/main/res/raw/torrc b/bramble-android/src/main/res/raw/torrc
index d1f881dce7..3d27a7f205 100644
--- a/bramble-android/src/main/res/raw/torrc
+++ b/bramble-android/src/main/res/raw/torrc
@@ -4,8 +4,3 @@ DisableNetwork 1
 RunAsDaemon 1
 SafeSocks 1
 SocksPort 59050
-UseBridges 0
-Bridge 131.252.210.150:8081 0E858AC201BF0F3FA3C462F64844CBFFC7297A42
-Bridge 128.105.214.161:8081 1E326AAFB3FCB515015250D8FCCC8E37F91A153B
-Bridge 67.205.189.122:8443 12D64D5D44E20169585E7378580C0D33A872AD98
-Bridge 45.32.148.146:8443 0CE016FB2462D8BF179AE71F7D702D09DEAC3F1D
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java
index 03470f4f26..3cb8663484 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java
@@ -25,6 +25,7 @@ import org.briarproject.bramble.api.system.LocationUtils;
 import org.briarproject.bramble.api.system.Scheduler;
 import org.briarproject.bramble.plugin.bluetooth.AndroidBluetoothPluginFactory;
 import org.briarproject.bramble.plugin.tcp.AndroidLanTcpPluginFactory;
+import org.briarproject.bramble.plugin.tor.BridgeProvider;
 import org.briarproject.bramble.plugin.tor.TorPluginFactory;
 import org.briarproject.bramble.util.AndroidUtils;
 import org.briarproject.bramble.util.StringUtils;
@@ -99,14 +100,14 @@ public class AppModule {
 			AndroidExecutor androidExecutor, SecureRandom random,
 			SocketFactory torSocketFactory, BackoffFactory backoffFactory,
 			Application app, LocationUtils locationUtils, EventBus eventBus,
-			Clock clock) {
+			BridgeProvider bridgeProvider, Clock clock) {
 		Context appContext = app.getApplicationContext();
 		DuplexPluginFactory bluetooth =
 				new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor,
 						appContext, random, eventBus, backoffFactory);
 		DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, scheduler,
 				appContext, locationUtils, eventBus, torSocketFactory,
-				backoffFactory, clock);
+				backoffFactory, bridgeProvider, clock);
 		DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
 				scheduler, backoffFactory, appContext);
 		Collection<DuplexPluginFactory> duplex = asList(bluetooth, tor, lan);
-- 
GitLab