diff --git a/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java b/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java index 7fa8a4b39d25a7eec7ec0afbe7da85bcc0a96b28..2228b8b5b18c1155a0e4e583a7890149ff401a70 100644 --- a/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java +++ b/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java @@ -15,7 +15,7 @@ import org.briarproject.api.plugins.simplex.SimplexPluginConfig; import org.briarproject.api.plugins.simplex.SimplexPluginFactory; import org.briarproject.api.system.LocationUtils; import org.briarproject.plugins.droidtooth.DroidtoothPluginFactory; -import org.briarproject.plugins.tcp.LanTcpPluginFactory; +import org.briarproject.plugins.tcp.AndroidLanTcpPluginFactory; import org.briarproject.plugins.tor.TorPluginFactory; import android.app.Application; @@ -44,14 +44,15 @@ public class AndroidPluginsModule extends AbstractModule { CryptoComponent crypto, LocationUtils locationUtils, ShutdownManager shutdownManager) { Context appContext = app.getApplicationContext(); - DuplexPluginFactory droidtooth = new DroidtoothPluginFactory( + DuplexPluginFactory bluetooth = new DroidtoothPluginFactory( pluginExecutor, androidExecutor, appContext, crypto.getSecureRandom()); DuplexPluginFactory tor = new TorPluginFactory(pluginExecutor, appContext, locationUtils, shutdownManager); - DuplexPluginFactory lan = new LanTcpPluginFactory(pluginExecutor); + DuplexPluginFactory lan = new AndroidLanTcpPluginFactory( + pluginExecutor, appContext); final Collection<DuplexPluginFactory> factories = - Arrays.asList(droidtooth, tor, lan); + Arrays.asList(bluetooth, tor, lan); return new DuplexPluginConfig() { public Collection<DuplexPluginFactory> getFactories() { return factories; diff --git a/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPlugin.java b/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPlugin.java new file mode 100644 index 0000000000000000000000000000000000000000..5092d4537744f0157d2709349ea881eca553ac9b --- /dev/null +++ b/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPlugin.java @@ -0,0 +1,71 @@ +package org.briarproject.plugins.tcp; + +import static android.content.Context.CONNECTIVITY_SERVICE; +import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.TYPE_WIFI; + +import java.util.concurrent.Executor; +import java.util.logging.Logger; + +import org.briarproject.api.plugins.duplex.DuplexPluginCallback; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; + +class AndroidLanTcpPlugin extends LanTcpPlugin { + + private static final Logger LOG = + Logger.getLogger(AndroidLanTcpPlugin.class.getName()); + + private final Context appContext; + + private volatile BroadcastReceiver networkStateReceiver = null; + + AndroidLanTcpPlugin(Executor pluginExecutor, Context appContext, + DuplexPluginCallback callback, int maxFrameLength, long maxLatency, + long pollingInterval) { + super(pluginExecutor, callback, maxFrameLength, maxLatency, + pollingInterval); + this.appContext = appContext; + } + + @Override + public boolean start() { + running = true; + // Register to receive network status events + networkStateReceiver = new NetworkStateReceiver(); + IntentFilter filter = new IntentFilter(CONNECTIVITY_ACTION); + appContext.registerReceiver(networkStateReceiver, filter); + return true; + } + + @Override + public void stop() { + running = false; + if(networkStateReceiver != null) + appContext.unregisterReceiver(networkStateReceiver); + tryToClose(socket); + } + + private class NetworkStateReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context ctx, Intent i) { + if(!running) return; + Object o = ctx.getSystemService(CONNECTIVITY_SERVICE); + ConnectivityManager cm = (ConnectivityManager) o; + NetworkInfo net = cm.getActiveNetworkInfo(); + if(net != null && net.getType() == TYPE_WIFI && net.isConnected()) { + LOG.info("Connected to Wi-Fi"); + if(socket == null || socket.isClosed()) bind(); + } else { + LOG.info("Not connected to Wi-Fi"); + tryToClose(socket); + } + } + } +} diff --git a/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPluginFactory.java b/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPluginFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..dec3bf711e0f03b0773d45c15fcc6a0f5676d73b --- /dev/null +++ b/briar-android/src/org/briarproject/plugins/tcp/AndroidLanTcpPluginFactory.java @@ -0,0 +1,35 @@ +package org.briarproject.plugins.tcp; + +import java.util.concurrent.Executor; + +import org.briarproject.api.TransportId; +import org.briarproject.api.plugins.duplex.DuplexPlugin; +import org.briarproject.api.plugins.duplex.DuplexPluginCallback; +import org.briarproject.api.plugins.duplex.DuplexPluginFactory; + +import android.content.Context; + +public class AndroidLanTcpPluginFactory implements DuplexPluginFactory { + + private static final int MAX_FRAME_LENGTH = 1024; + 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; + + public AndroidLanTcpPluginFactory(Executor pluginExecutor, + Context appContext) { + this.pluginExecutor = pluginExecutor; + this.appContext = appContext; + } + + public TransportId getId() { + return LanTcpPlugin.ID; + } + + public DuplexPlugin createPlugin(DuplexPluginCallback callback) { + return new AndroidLanTcpPlugin(pluginExecutor, appContext, callback, + MAX_FRAME_LENGTH, MAX_LATENCY, POLLING_INTERVAL); + } +} diff --git a/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java b/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java index da0293603b89ecca7eb86c85f6a6aac04bd172a9..a03bd6155def5185110a1ca45bc705b012ec1f50 100644 --- a/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java +++ b/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java @@ -18,7 +18,6 @@ import org.briarproject.api.TransportId; import org.briarproject.api.TransportProperties; import org.briarproject.api.plugins.duplex.DuplexPluginCallback; -/** A TCP plugin that supports exchanging invitations over a LAN. */ class LanTcpPlugin extends TcpPlugin { static final TransportId ID = new TransportId("lan"); diff --git a/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java b/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java index 9c062d378596fa6e862b4c1ce767a77a828086bb..13d913ce84f414320fa24a7c814156f2840e6fb0 100644 --- a/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java +++ b/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java @@ -38,7 +38,7 @@ abstract class TcpPlugin implements DuplexPlugin { protected final long maxLatency, pollingInterval; protected volatile boolean running = false; - private volatile ServerSocket socket = null; + protected volatile ServerSocket socket = null; /** * Returns zero or more socket addresses on which the plugin should listen, @@ -65,47 +65,47 @@ abstract class TcpPlugin implements DuplexPlugin { public boolean start() { running = true; - pluginExecutor.execute(new Runnable() { - public void run() { - bind(); - } - }); + bind(); return true; } - private void bind() { - ServerSocket ss = null; - boolean found = false; - for(SocketAddress addr : getLocalSocketAddresses()) { - try { - ss = new ServerSocket(); - ss.bind(addr); - found = true; - break; - } catch(IOException e) { - if(LOG.isLoggable(INFO)) LOG.info("Failed to bind " + addr); - tryToClose(ss); - continue; + protected void bind() { + pluginExecutor.execute(new Runnable() { + public void run() { + if(!running) return; + ServerSocket ss = null; + for(SocketAddress addr : getLocalSocketAddresses()) { + try { + ss = new ServerSocket(); + ss.bind(addr); + break; + } catch(IOException e) { + if(LOG.isLoggable(INFO)) + LOG.info("Failed to bind " + addr); + tryToClose(ss); + continue; + } + } + if(ss == null || !ss.isBound()) { + LOG.info("Could not bind server socket"); + return; + } + if(!running) { + tryToClose(ss); + return; + } + socket = ss; + SocketAddress local = ss.getLocalSocketAddress(); + setLocalSocketAddress((InetSocketAddress) local); + if(LOG.isLoggable(INFO)) LOG.info("Listening on " + local); + acceptContactConnections(); } - } - if(!found) { - LOG.info("Could not bind server socket"); - return; - } - if(!running) { - tryToClose(ss); - return; - } - socket = ss; - if(LOG.isLoggable(INFO)) - LOG.info("Listening on " + ss.getLocalSocketAddress()); - setLocalSocketAddress((InetSocketAddress) ss.getLocalSocketAddress()); - acceptContactConnections(ss); + }); } protected void tryToClose(ServerSocket ss) { try { - ss.close(); + if(ss != null) ss.close(); } catch(IOException e) { if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); } @@ -124,32 +124,31 @@ abstract class TcpPlugin implements DuplexPlugin { callback.mergeLocalProperties(p); } - private void acceptContactConnections(ServerSocket ss) { - while(true) { + private void acceptContactConnections() { + while(isRunning()) { Socket s; try { - s = ss.accept(); + s = socket.accept(); } catch(IOException e) { // This is expected when the socket is closed if(LOG.isLoggable(INFO)) LOG.info(e.toString()); - tryToClose(ss); + tryToClose(socket); return; } if(LOG.isLoggable(INFO)) LOG.info("Connection from " + s.getRemoteSocketAddress()); TcpTransportConnection conn = new TcpTransportConnection(this, s); callback.incomingConnectionCreated(conn); - if(!running) return; } } public void stop() { running = false; - if(socket != null) tryToClose(socket); + tryToClose(socket); } public boolean isRunning() { - return running && socket != null && socket.isBound(); + return running && socket != null && !socket.isClosed(); } public boolean shouldPoll() { @@ -161,7 +160,7 @@ abstract class TcpPlugin implements DuplexPlugin { } public void poll(Collection<ContactId> connected) { - if(!running) return; + if(!isRunning()) return; Map<ContactId, TransportProperties> remote = callback.getRemoteProperties(); for(final ContactId c : remote.keySet()) { @@ -180,7 +179,7 @@ abstract class TcpPlugin implements DuplexPlugin { } public DuplexTransportConnection createConnection(ContactId c) { - if(!running) return null; + if(!isRunning()) return null; SocketAddress addr = getRemoteSocketAddress(c); if(addr == null) return null; Socket s = new Socket();