From e7fc37d81e26897c4c2214b49d6c9c694f352404 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Tue, 19 Apr 2022 11:03:08 +0100 Subject: [PATCH 1/3] Catch SecurityExceptions from all ConnectivityManager calls. This issue occurs on Android 11 and no fix is expected. When the issue occurs, Tor connectivity and outgoing LAN connectivity will be broken until the app is restarted. --- .../network/AndroidNetworkManager.java | 58 +++++++++------ .../plugin/tcp/AndroidLanTcpPlugin.java | 41 +++++++---- .../reporting/BriarReportCollector.java | 73 ++++++++++--------- 3 files changed, 101 insertions(+), 71 deletions(-) diff --git a/bramble-android/src/main/java/org/briarproject/bramble/network/AndroidNetworkManager.java b/bramble-android/src/main/java/org/briarproject/bramble/network/AndroidNetworkManager.java index dd6f91018..2f1195fc6 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/network/AndroidNetworkManager.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/network/AndroidNetworkManager.java @@ -111,15 +111,21 @@ class AndroidNetworkManager implements NetworkManager, Service { @Override public NetworkStatus getNetworkStatus() { - NetworkInfo net = connectivityManager.getActiveNetworkInfo(); - boolean connected = net != null && net.isConnected(); - boolean wifi = false, ipv6Only = false; - if (connected) { - wifi = net.getType() == TYPE_WIFI; - if (SDK_INT >= 23) ipv6Only = isActiveNetworkIpv6Only(); - else ipv6Only = areAllAvailableNetworksIpv6Only(); + // https://issuetracker.google.com/issues/175055271 + try { + NetworkInfo net = connectivityManager.getActiveNetworkInfo(); + boolean connected = net != null && net.isConnected(); + boolean wifi = false, ipv6Only = false; + if (connected) { + wifi = net.getType() == TYPE_WIFI; + if (SDK_INT >= 23) ipv6Only = isActiveNetworkIpv6Only(); + else ipv6Only = areAllAvailableNetworksIpv6Only(); + } + return new NetworkStatus(connected, wifi, ipv6Only); + } catch (SecurityException e) { + logException(LOG, WARNING, e); + return new NetworkStatus(false, false, false); } - return new NetworkStatus(connected, wifi, ipv6Only); } /** @@ -130,23 +136,29 @@ class AndroidNetworkManager implements NetworkManager, Service { */ @TargetApi(23) private boolean isActiveNetworkIpv6Only() { - Network net = connectivityManager.getActiveNetwork(); - if (net == null) { - LOG.info("No active network"); + // https://issuetracker.google.com/issues/175055271 + try { + Network net = connectivityManager.getActiveNetwork(); + if (net == null) { + LOG.info("No active network"); + return false; + } + LinkProperties props = connectivityManager.getLinkProperties(net); + if (props == null) { + LOG.info("No link properties for active network"); + return false; + } + boolean hasIpv6Unicast = false; + for (LinkAddress linkAddress : props.getLinkAddresses()) { + InetAddress addr = linkAddress.getAddress(); + if (addr instanceof Inet4Address) return false; + if (!addr.isMulticastAddress()) hasIpv6Unicast = true; + } + return hasIpv6Unicast; + } catch (SecurityException e) { + logException(LOG, WARNING, e); return false; } - LinkProperties props = connectivityManager.getLinkProperties(net); - if (props == null) { - LOG.info("No link properties for active network"); - return false; - } - boolean hasIpv6Unicast = false; - for (LinkAddress linkAddress : props.getLinkAddresses()) { - InetAddress addr = linkAddress.getAddress(); - if (addr instanceof Inet4Address) return false; - if (!addr.isMulticastAddress()) hasIpv6Unicast = true; - } - return hasIpv6Unicast; } /** 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 22dfbe8df..11e164148 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 @@ -175,16 +175,24 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { @TargetApi(21) @Nullable private InetAddress getWifiClientIpv6Address() { - for (Network net : connectivityManager.getAllNetworks()) { - NetworkCapabilities caps = - connectivityManager.getNetworkCapabilities(net); - if (caps == null || !caps.hasTransport(TRANSPORT_WIFI)) continue; - LinkProperties props = connectivityManager.getLinkProperties(net); - if (props == null) continue; - for (LinkAddress linkAddress : props.getLinkAddresses()) { - InetAddress addr = linkAddress.getAddress(); - if (isIpv6LinkLocalAddress(addr)) return addr; + // https://issuetracker.google.com/issues/175055271 + try { + for (Network net : connectivityManager.getAllNetworks()) { + NetworkCapabilities caps = + connectivityManager.getNetworkCapabilities(net); + if (caps == null || !caps.hasTransport(TRANSPORT_WIFI)) { + continue; + } + LinkProperties props = + connectivityManager.getLinkProperties(net); + if (props == null) continue; + for (LinkAddress linkAddress : props.getLinkAddresses()) { + InetAddress addr = linkAddress.getAddress(); + if (isIpv6LinkLocalAddress(addr)) return addr; + } } + } catch (SecurityException e) { + logException(LOG, WARNING, e); } return null; } @@ -227,12 +235,17 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { // 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()) { - NetworkCapabilities caps = - connectivityManager.getNetworkCapabilities(net); - if (caps != null && caps.hasTransport(TRANSPORT_WIFI)) { - return net.getSocketFactory(); + // https://issuetracker.google.com/issues/175055271 + try { + for (Network net : connectivityManager.getAllNetworks()) { + NetworkCapabilities caps = + connectivityManager.getNetworkCapabilities(net); + if (caps != null && caps.hasTransport(TRANSPORT_WIFI)) { + return net.getSocketFactory(); + } } + } catch (SecurityException e) { + logException(LOG, WARNING, e); } LOG.warning("Could not find suitable socket factory"); return SocketFactory.getDefault(); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportCollector.java b/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportCollector.java index b6ea21664..f3c5bf7e5 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportCollector.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportCollector.java @@ -198,38 +198,47 @@ class BriarReportCollector { private ReportItem getConnectivity() { MultiReportInfo connectivityInfo = new MultiReportInfo(); - // Is mobile data available? - ConnectivityManager cm = requireNonNull( - getSystemService(ctx, ConnectivityManager.class)); - NetworkInfo mobile = cm.getNetworkInfo(TYPE_MOBILE); - boolean mobileAvailable = mobile != null && mobile.isAvailable(); - connectivityInfo.add("MobileDataAvailable", mobileAvailable); - - // Is mobile data enabled? - boolean mobileEnabled = false; + // https://issuetracker.google.com/issues/175055271 try { - Class clazz = Class.forName(cm.getClass().getName()); - Method method = clazz.getDeclaredMethod("getMobileDataEnabled"); - method.setAccessible(true); - mobileEnabled = (Boolean) requireNonNull(method.invoke(cm)); - } catch (ClassNotFoundException - | NoSuchMethodException - | IllegalArgumentException - | InvocationTargetException - | IllegalAccessException e) { - connectivityInfo - .add("MobileDataReflectionException", e.toString()); + // Is mobile data available? + ConnectivityManager cm = requireNonNull( + getSystemService(ctx, ConnectivityManager.class)); + NetworkInfo mobile = cm.getNetworkInfo(TYPE_MOBILE); + boolean mobileAvailable = mobile != null && mobile.isAvailable(); + connectivityInfo.add("MobileDataAvailable", mobileAvailable); + + // Is mobile data enabled? + boolean mobileEnabled = false; + try { + Class clazz = Class.forName(cm.getClass().getName()); + Method method = clazz.getDeclaredMethod("getMobileDataEnabled"); + method.setAccessible(true); + mobileEnabled = (Boolean) requireNonNull(method.invoke(cm)); + } catch (ClassNotFoundException + | NoSuchMethodException + | IllegalArgumentException + | InvocationTargetException + | IllegalAccessException e) { + connectivityInfo + .add("MobileDataReflectionException", e.toString()); + } + connectivityInfo.add("MobileDataEnabled", mobileEnabled); + + // Is mobile data connected ? + boolean mobileConnected = mobile != null && mobile.isConnected(); + connectivityInfo.add("MobileDataConnected", mobileConnected); + + // Is wifi available? + NetworkInfo wifi = cm.getNetworkInfo(TYPE_WIFI); + boolean wifiAvailable = wifi != null && wifi.isAvailable(); + connectivityInfo.add("WifiAvailable", wifiAvailable); + + // Is wifi connected? + boolean wifiConnected = wifi != null && wifi.isConnected(); + connectivityInfo.add("WifiConnected", wifiConnected); + } catch (SecurityException e) { + connectivityInfo.add("ConnectivityManagerException", e.toString()); } - connectivityInfo.add("MobileDataEnabled", mobileEnabled); - - // Is mobile data connected ? - boolean mobileConnected = mobile != null && mobile.isConnected(); - connectivityInfo.add("MobileDataConnected", mobileConnected); - - // Is wifi available? - NetworkInfo wifi = cm.getNetworkInfo(TYPE_WIFI); - boolean wifiAvailable = wifi != null && wifi.isAvailable(); - connectivityInfo.add("WifiAvailable", wifiAvailable); // Is wifi enabled? WifiManager wm = getSystemService(ctx, WifiManager.class); @@ -237,10 +246,6 @@ class BriarReportCollector { wm.getWifiState() == WIFI_STATE_ENABLED; connectivityInfo.add("WifiEnabled", wifiEnabled); - // Is wifi connected? - boolean wifiConnected = wifi != null && wifi.isConnected(); - connectivityInfo.add("WifiConnected", wifiConnected); - // Is wifi direct supported? boolean wifiDirect = ctx.getSystemService(WIFI_P2P_SERVICE) != null; connectivityInfo.add("WiFiDirectSupported", wifiDirect); From a8624cd50757a7ff4b8623224187b9a45bb8aa4c Mon Sep 17 00:00:00 2001 From: akwizgran Date: Tue, 19 Apr 2022 11:27:40 +0100 Subject: [PATCH 2/3] Guess connectivity when ConnectivityManager is broken. --- .../network/AndroidNetworkManager.java | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/bramble-android/src/main/java/org/briarproject/bramble/network/AndroidNetworkManager.java b/bramble-android/src/main/java/org/briarproject/bramble/network/AndroidNetworkManager.java index 2f1195fc6..944fa608a 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/network/AndroidNetworkManager.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/network/AndroidNetworkManager.java @@ -11,6 +11,8 @@ import android.net.LinkAddress; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkInfo; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventExecutor; @@ -38,6 +40,7 @@ import javax.annotation.Nullable; import javax.inject.Inject; import static android.content.Context.CONNECTIVITY_SERVICE; +import static android.content.Context.WIFI_SERVICE; import static android.content.Intent.ACTION_SCREEN_OFF; import static android.content.Intent.ACTION_SCREEN_ON; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; @@ -124,7 +127,30 @@ class AndroidNetworkManager implements NetworkManager, Service { return new NetworkStatus(connected, wifi, ipv6Only); } catch (SecurityException e) { logException(LOG, WARNING, e); - return new NetworkStatus(false, false, false); + // Without the ConnectivityManager we can't detect whether we have + // internet access. Assume we do, which is probably less harmful + // than assuming we don't. Likewise, assume the connection is + // IPv6-only. Fall back to the WifiManager to detect whether we + // have a wifi connection. + LOG.info("ConnectivityManager is broken, guessing connectivity"); + boolean connected = true, wifi = false, ipv6Only = true; + WifiManager wm = (WifiManager) app.getSystemService(WIFI_SERVICE); + if (wm != null) { + WifiInfo info = wm.getConnectionInfo(); + if (info == null) { + LOG.info("Not connected to wifi"); + } else { + LOG.info("Connected to wifi"); + wifi = true; + if (info.getIpAddress() == 0) { + LOG.info("No IPv4 address"); + } else { + LOG.info("Found an IPv4 address"); + ipv6Only = false; + } + } + } + return new NetworkStatus(connected, wifi, ipv6Only); } } From afff66eaffc714fa4fbe6ae3f7d537c3a0acf5e4 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Wed, 20 Apr 2022 12:42:35 +0100 Subject: [PATCH 3/3] Don't assume that non-null WifiInfo means we're connected to wifi. --- .../bramble/network/AndroidNetworkManager.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/bramble-android/src/main/java/org/briarproject/bramble/network/AndroidNetworkManager.java b/bramble-android/src/main/java/org/briarproject/bramble/network/AndroidNetworkManager.java index 944fa608a..02a6d38e2 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/network/AndroidNetworkManager.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/network/AndroidNetworkManager.java @@ -137,17 +137,10 @@ class AndroidNetworkManager implements NetworkManager, Service { WifiManager wm = (WifiManager) app.getSystemService(WIFI_SERVICE); if (wm != null) { WifiInfo info = wm.getConnectionInfo(); - if (info == null) { - LOG.info("Not connected to wifi"); - } else { + if (info != null && info.getIpAddress() != 0) { LOG.info("Connected to wifi"); wifi = true; - if (info.getIpAddress() == 0) { - LOG.info("No IPv4 address"); - } else { - LOG.info("Found an IPv4 address"); - ipv6Only = false; - } + ipv6Only = false; } } return new NetworkStatus(connected, wifi, ipv6Only);