From edbb0a3c1375c5b97d91dee861f21bfb1184eda4 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 26 Mar 2018 13:58:10 +0100 Subject: [PATCH 1/2] Use WifiManager to get wifi network information. This ensures we bind to the wifi interface even if it doesn't have internet access and there's another interface with internet access (e.g. mobile data). --- .../plugin/tcp/AndroidLanTcpPlugin.java | 54 ++++++++++++++----- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPlugin.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPlugin.java index 4d507d0d1..ca6b4f6a8 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPlugin.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPlugin.java @@ -4,21 +4,25 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Collection; import java.util.concurrent.Executor; import java.util.logging.Logger; import javax.annotation.Nullable; -import static android.content.Context.CONNECTIVITY_SERVICE; +import static android.content.Context.WIFI_SERVICE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; -import static android.net.ConnectivityManager.TYPE_WIFI; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; @NotNullByDefault class AndroidLanTcpPlugin extends LanTcpPlugin { @@ -27,6 +31,8 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { Logger.getLogger(AndroidLanTcpPlugin.class.getName()); private final Context appContext; + @Nullable + private final WifiManager wifiManager; @Nullable private volatile BroadcastReceiver networkStateReceiver = null; @@ -36,6 +42,8 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { int maxIdleTime) { super(ioExecutor, backoff, callback, maxLatency, maxIdleTime); this.appContext = appContext; + wifiManager = (WifiManager) appContext.getApplicationContext() + .getSystemService(WIFI_SERVICE); } @Override @@ -56,20 +64,40 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { tryToClose(socket); } + @Override + protected Collection getLocalIpAddresses() { + if (wifiManager == null) return emptyList(); + WifiInfo info = wifiManager.getConnectionInfo(); + if (info == null || info.getIpAddress() == 0) return emptyList(); + return singletonList(intToInetAddress(info.getIpAddress())); + } + + private InetAddress intToInetAddress(int ip) { + byte[] ipBytes = new byte[4]; + ipBytes[0] = (byte) (ip & 0xFF); + ipBytes[1] = (byte) ((ip >> 8) & 0xFF); + ipBytes[2] = (byte) ((ip >> 16) & 0xFF); + ipBytes[3] = (byte) ((ip >> 24) & 0xFF); + try { + return InetAddress.getByAddress(ipBytes); + } catch (UnknownHostException e) { + // Should only be thrown if address has illegal length + throw new AssertionError(e); + } + } + 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"); + if (!running || wifiManager == null) return; + WifiInfo info = wifiManager.getConnectionInfo(); + if (info == null || info.getIpAddress() == 0) { + LOG.info("Not connected to wifi"); tryToClose(socket); + } else { + LOG.info("Connected to wifi"); + if (socket == null || socket.isClosed()) bind(); } } } From ba99f5855910252e6f5c8efc439e188a3e4eb4ca Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 26 Mar 2018 16:53:31 +0100 Subject: [PATCH 2/2] Use wifi network's socket factory on API 21+. --- .../plugin/tcp/AndroidLanTcpPlugin.java | 36 +++++++++++++++++++ .../bramble/plugin/tcp/LanTcpPlugin.java | 3 +- .../bramble/plugin/tcp/TcpPlugin.java | 7 +++- 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPlugin.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPlugin.java index ca6b4f6a8..c6eba28c7 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPlugin.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPlugin.java @@ -4,6 +4,9 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkInfo; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; @@ -11,16 +14,22 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback; +import java.io.IOException; import java.net.InetAddress; +import java.net.Socket; import java.net.UnknownHostException; import java.util.Collection; import java.util.concurrent.Executor; import java.util.logging.Logger; import javax.annotation.Nullable; +import javax.net.SocketFactory; +import static android.content.Context.CONNECTIVITY_SERVICE; import static android.content.Context.WIFI_SERVICE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.TYPE_WIFI; +import static android.os.Build.VERSION.SDK_INT; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; @@ -31,19 +40,26 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { Logger.getLogger(AndroidLanTcpPlugin.class.getName()); private final Context appContext; + private final ConnectivityManager connectivityManager; @Nullable private final WifiManager wifiManager; @Nullable private volatile BroadcastReceiver networkStateReceiver = null; + private volatile SocketFactory socketFactory; AndroidLanTcpPlugin(Executor ioExecutor, Backoff backoff, Context appContext, DuplexPluginCallback callback, int maxLatency, int maxIdleTime) { super(ioExecutor, backoff, callback, maxLatency, maxIdleTime); this.appContext = appContext; + ConnectivityManager connectivityManager = (ConnectivityManager) + appContext.getSystemService(CONNECTIVITY_SERVICE); + if (connectivityManager == null) throw new AssertionError(); + this.connectivityManager = connectivityManager; wifiManager = (WifiManager) appContext.getApplicationContext() .getSystemService(WIFI_SERVICE); + socketFactory = getSocketFactory(); } @Override @@ -64,6 +80,11 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { tryToClose(socket); } + @Override + protected Socket createSocket() throws IOException { + return socketFactory.createSocket(); + } + @Override protected Collection getLocalIpAddresses() { if (wifiManager == null) return emptyList(); @@ -86,6 +107,19 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { } } + // On API 21 and later, a socket that is not created with the wifi + // network's socket factory may try to connect via another network + private SocketFactory getSocketFactory() { + if (SDK_INT < 21) return SocketFactory.getDefault(); + for (Network net : connectivityManager.getAllNetworks()) { + NetworkInfo info = connectivityManager.getNetworkInfo(net); + if (info != null && info.getType() == TYPE_WIFI) + return net.getSocketFactory(); + } + LOG.warning("Could not find suitable socket factory"); + return SocketFactory.getDefault(); + } + private class NetworkStateReceiver extends BroadcastReceiver { @Override @@ -94,9 +128,11 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { WifiInfo info = wifiManager.getConnectionInfo(); if (info == null || info.getIpAddress() == 0) { LOG.info("Not connected to wifi"); + socketFactory = SocketFactory.getDefault(); tryToClose(socket); } else { LOG.info("Connected to wifi"); + socketFactory = getSocketFactory(); if (socket == null || socket.isClosed()) bind(); } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java index 450367381..192a43c3d 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java @@ -241,10 +241,11 @@ class LanTcpPlugin extends TcpPlugin { } return null; } - Socket s = new Socket(); try { if (LOG.isLoggable(INFO)) LOG.info("Connecting to " + scrubSocketAddress(remote)); + Socket s = createSocket(); + s.bind(new InetSocketAddress(socket.getInetAddress(), 0)); s.connect(remote); s.setSoTimeout(socketTimeout); if (LOG.isLoggable(INFO)) diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java index 9b686fc86..8b861164d 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java @@ -243,10 +243,11 @@ abstract class TcpPlugin implements DuplexPlugin { } continue; } - Socket s = new Socket(); try { if (LOG.isLoggable(INFO)) LOG.info("Connecting to " + scrubSocketAddress(remote)); + Socket s = createSocket(); + s.bind(new InetSocketAddress(socket.getInetAddress(), 0)); s.connect(remote); s.setSoTimeout(socketTimeout); if (LOG.isLoggable(INFO)) @@ -261,6 +262,10 @@ abstract class TcpPlugin implements DuplexPlugin { return null; } + protected Socket createSocket() throws IOException { + return new Socket(); + } + @Nullable InetSocketAddress parseSocketAddress(String ipPort) { if (StringUtils.isNullOrEmpty(ipPort)) return null;