From 91ccdfd8d773257edacb134c6df2377019fcc601 Mon Sep 17 00:00:00 2001
From: akwizgran <michael@briarproject.org>
Date: Thu, 16 May 2013 13:48:42 +0100
Subject: [PATCH] Created Android-specific LAN TCP plugin in order to enable
 multicast.

Some Android devices require a lock to be held while using multicast, to
disable the packet filter that normally filters out multicast packets.
---
 briar-android/AndroidManifest.xml             |  1 +
 .../net/sf/briar/android/AndroidModule.java   |  5 +-
 .../briar/plugins/tcp/DroidLanTcpPlugin.java  | 55 +++++++++++++++++++
 .../plugins/tcp/DroidLanTcpPluginFactory.java | 37 +++++++++++++
 .../sf/briar/plugins/tcp/LanTcpPlugin.java    | 13 +++++
 5 files changed, 109 insertions(+), 2 deletions(-)
 create mode 100644 briar-core/src/net/sf/briar/plugins/tcp/DroidLanTcpPlugin.java
 create mode 100644 briar-core/src/net/sf/briar/plugins/tcp/DroidLanTcpPluginFactory.java

diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml
index 2f5e0d87b5..70d79d2fa9 100644
--- a/briar-android/AndroidManifest.xml
+++ b/briar-android/AndroidManifest.xml
@@ -9,6 +9,7 @@
 	<uses-permission android:name="android.permission.BLUETOOTH" />
 	<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
 	<uses-permission android:name="android.permission.INTERNET" />
+	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 	<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
 	<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
 
diff --git a/briar-android/src/net/sf/briar/android/AndroidModule.java b/briar-android/src/net/sf/briar/android/AndroidModule.java
index 06dbc4bcfa..afac6d63c6 100644
--- a/briar-android/src/net/sf/briar/android/AndroidModule.java
+++ b/briar-android/src/net/sf/briar/android/AndroidModule.java
@@ -23,7 +23,7 @@ import net.sf.briar.api.plugins.duplex.DuplexPluginFactory;
 import net.sf.briar.api.plugins.simplex.SimplexPluginConfig;
 import net.sf.briar.api.plugins.simplex.SimplexPluginFactory;
 import net.sf.briar.plugins.droidtooth.DroidtoothPluginFactory;
-import net.sf.briar.plugins.tcp.LanTcpPluginFactory;
+import net.sf.briar.plugins.tcp.DroidLanTcpPluginFactory;
 import net.sf.briar.plugins.tcp.WanTcpPluginFactory;
 import net.sf.briar.plugins.tor.TorPluginFactory;
 import android.content.Context;
@@ -71,7 +71,8 @@ public class AndroidModule extends AbstractModule {
 				crypto.getSecureRandom());
 		DuplexPluginFactory tor = new TorPluginFactory(pluginExecutor,
 				appContext, shutdownManager);
-		DuplexPluginFactory lan = new LanTcpPluginFactory(pluginExecutor);
+		DuplexPluginFactory lan = new DroidLanTcpPluginFactory(pluginExecutor,
+				appContext);
 		DuplexPluginFactory wan = new WanTcpPluginFactory(pluginExecutor,
 				shutdownManager);
 		final Collection<DuplexPluginFactory> factories =
diff --git a/briar-core/src/net/sf/briar/plugins/tcp/DroidLanTcpPlugin.java b/briar-core/src/net/sf/briar/plugins/tcp/DroidLanTcpPlugin.java
new file mode 100644
index 0000000000..818472cbd7
--- /dev/null
+++ b/briar-core/src/net/sf/briar/plugins/tcp/DroidLanTcpPlugin.java
@@ -0,0 +1,55 @@
+package net.sf.briar.plugins.tcp;
+
+import static android.content.Context.WIFI_SERVICE;
+
+import java.util.concurrent.Executor;
+
+import net.sf.briar.api.clock.Clock;
+import net.sf.briar.api.crypto.PseudoRandom;
+import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
+import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
+import android.content.Context;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiManager.MulticastLock;
+
+class DroidLanTcpPlugin extends LanTcpPlugin {
+
+	private final Context appContext;
+
+	DroidLanTcpPlugin(Executor pluginExecutor, Context appContext, Clock clock,
+			DuplexPluginCallback callback, long maxLatency,
+			long pollingInterval) {
+		super(pluginExecutor, clock, callback, maxLatency, pollingInterval);
+		this.appContext = appContext;
+	}
+
+	@Override
+	public DuplexTransportConnection sendInvitation(PseudoRandom r,
+			long timeout) {
+		WifiManager wifi =
+				(WifiManager) appContext.getSystemService(WIFI_SERVICE);
+		if(wifi == null || !wifi.isWifiEnabled()) return null;
+		MulticastLock lock = wifi.createMulticastLock("invitation");
+		lock.acquire();
+		try {
+			return super.sendInvitation(r, timeout);
+		} finally {
+			lock.release();
+		}
+	}
+
+	@Override
+	public DuplexTransportConnection acceptInvitation(PseudoRandom r,
+			long timeout) {
+		WifiManager wifi =
+				(WifiManager) appContext.getSystemService(WIFI_SERVICE);
+		if(wifi == null || !wifi.isWifiEnabled()) return null;
+		MulticastLock lock = wifi.createMulticastLock("invitation");
+		lock.acquire();
+		try {
+			return super.acceptInvitation(r, timeout);
+		} finally {
+			lock.release();
+		}
+	}
+}
diff --git a/briar-core/src/net/sf/briar/plugins/tcp/DroidLanTcpPluginFactory.java b/briar-core/src/net/sf/briar/plugins/tcp/DroidLanTcpPluginFactory.java
new file mode 100644
index 0000000000..44d4881658
--- /dev/null
+++ b/briar-core/src/net/sf/briar/plugins/tcp/DroidLanTcpPluginFactory.java
@@ -0,0 +1,37 @@
+package net.sf.briar.plugins.tcp;
+
+import java.util.concurrent.Executor;
+
+import net.sf.briar.api.TransportId;
+import net.sf.briar.api.clock.Clock;
+import net.sf.briar.api.clock.SystemClock;
+import net.sf.briar.api.plugins.duplex.DuplexPlugin;
+import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
+import net.sf.briar.api.plugins.duplex.DuplexPluginFactory;
+import android.content.Context;
+
+public class DroidLanTcpPluginFactory implements DuplexPluginFactory {
+
+	private static final long MAX_LATENCY = 60 * 1000; // 1 minute
+	private static final long POLLING_INTERVAL = 60 * 1000; // 1 minute
+
+	private final Executor pluginExecutor;
+	private final Context appContext;
+	private final Clock clock;
+
+	public DroidLanTcpPluginFactory(Executor pluginExecutor,
+			Context appContext) {
+		this.pluginExecutor = pluginExecutor;
+		this.appContext = appContext;
+		clock = new SystemClock();
+	}
+
+	public TransportId getId() {
+		return LanTcpPlugin.ID;
+	}
+
+	public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
+		return new DroidLanTcpPlugin(pluginExecutor, appContext, clock,
+				callback, MAX_LATENCY, POLLING_INTERVAL);
+	}
+}
diff --git a/briar-core/src/net/sf/briar/plugins/tcp/LanTcpPlugin.java b/briar-core/src/net/sf/briar/plugins/tcp/LanTcpPlugin.java
index aeb307c6f4..b81250fd13 100644
--- a/briar-core/src/net/sf/briar/plugins/tcp/LanTcpPlugin.java
+++ b/briar-core/src/net/sf/briar/plugins/tcp/LanTcpPlugin.java
@@ -136,6 +136,7 @@ class LanTcpPlugin extends TcpPlugin {
 			if(ms != null) tryToClose(ms, mcast.getAddress());
 			return null;
 		}
+		if(LOG.isLoggable(INFO)) LOG.info("Listening for multicast packets");
 		// Listen until a valid packet is received or the timeout occurs
 		byte[] buffer = new byte[2];
 		DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
@@ -150,10 +151,15 @@ class LanTcpPlugin extends TcpPlugin {
 					int off = packet.getOffset();
 					int len = packet.getLength();
 					int port = parsePacket(b, off, len);
+					if(LOG.isLoggable(INFO)){
+						String addr = getHostAddress(packet.getAddress());
+						LOG.info("Received a packet from " + addr + ":" + port);
+					}
 					if(port >= 32768 && port < 65536) {
 						try {
 							// Connect back on the advertised TCP port
 							Socket s = new Socket(packet.getAddress(), port);
+							if(LOG.isLoggable(INFO)) LOG.info("Connected back");
 							return new TcpTransportConnection(s, maxLatency);
 						} catch(IOException e) {
 							if(LOG.isLoggable(WARNING))
@@ -161,6 +167,7 @@ class LanTcpPlugin extends TcpPlugin {
 						}
 					}
 				} catch(SocketTimeoutException e) {
+					if(LOG.isLoggable(INFO)) LOG.info("Timed out");
 					break;
 				}
 				now = clock.currentTimeMillis();
@@ -282,11 +289,17 @@ class LanTcpPlugin extends TcpPlugin {
 				try {
 					int wait = (int) (Math.min(end, nextPacket) - now);
 					ss.setSoTimeout(wait < 1 ? 1 : wait);
+					if(LOG.isLoggable(INFO))
+						LOG.info("Listening for TCP connections: " + wait);
 					Socket s = ss.accept();
+					if(LOG.isLoggable(INFO))
+						LOG.info("Received a TCP connection");
 					return new TcpTransportConnection(s, maxLatency);
 				} catch(SocketTimeoutException e) {
 					now = clock.currentTimeMillis();
 					if(now < end) {
+						if(LOG.isLoggable(INFO))
+							LOG.info("Sending multicast packet");
 						ms.send(packet);
 						now = clock.currentTimeMillis();
 						nextPacket = now + MULTICAST_INTERVAL;
-- 
GitLab