From 9aad3320cf58a19cb4bbbbbaa357dbedf8715900 Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Fri, 22 Nov 2024 14:15:57 -0600 Subject: [PATCH] mobile: Support for multiple transport types on the Android network monitor (#37321) In the previous implementation, we assumed that there was only a single transport type, which means the following use cases were not supported. [A, B] to [A], no network change [A] to [A, B], no network change This PR updates the network monitor code on Android to trigger network change appropriately when there are multiple transport types. IOW, the following use cases are now supported. [A, B] to [A], trigger network change [A] to [A, B], trigger network change Risk Level: low Testing: unit test Docs Changes: n/a Release Notes: n/a Platform Specific Features: mobile Signed-off-by: Fredy Wijaya --- .../engine/AndroidNetworkMonitor.java | 25 +++++++------ .../engine/AndroidNetworkMonitorTest.java | 36 ++++++++++++++++--- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/mobile/library/java/io/envoyproxy/envoymobile/engine/AndroidNetworkMonitor.java b/mobile/library/java/io/envoyproxy/envoymobile/engine/AndroidNetworkMonitor.java index 7ef76a865378..3b6b984e2bbe 100644 --- a/mobile/library/java/io/envoyproxy/envoymobile/engine/AndroidNetworkMonitor.java +++ b/mobile/library/java/io/envoyproxy/envoymobile/engine/AndroidNetworkMonitor.java @@ -15,7 +15,9 @@ import androidx.annotation.VisibleForTesting; import androidx.core.content.ContextCompat; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; /** * This class does the following. @@ -65,10 +67,9 @@ static class DefaultNetworkCallback extends NetworkCallback { NetworkCapabilities.TRANSPORT_BLUETOOTH, NetworkCapabilities.TRANSPORT_ETHERNET, NetworkCapabilities.TRANSPORT_VPN, NetworkCapabilities.TRANSPORT_WIFI_AWARE, }; - private static final int EMPTY_TRANSPORT_TYPE = -1; private final EnvoyEngine envoyEngine; - @VisibleForTesting int transportType = EMPTY_TRANSPORT_TYPE; + @VisibleForTesting List previousTransportTypes = new ArrayList<>(); DefaultNetworkCallback(EnvoyEngine envoyEngine) { this.envoyEngine = envoyEngine; } @@ -84,13 +85,14 @@ public void onCapabilitiesChanged(@NonNull Network network, // `onCapabilities` is guaranteed to be called immediately after `onAvailable` // starting with Android O, so this logic may not work on older Android versions. // https://developer.android.com/reference/android/net/ConnectivityManager.NetworkCallback#onCapabilitiesChanged(android.net.Network,%20android.net.NetworkCapabilities) - if (transportType == EMPTY_TRANSPORT_TYPE) { + if (previousTransportTypes.isEmpty()) { // The network was lost previously, see `onLost`. onDefaultNetworkChanged(networkCapabilities); } else { - // Only call the `onDefaultNetworkChanged` callback when there is a change in the - // transport type. - if (!networkCapabilities.hasTransport(transportType)) { + // Only call the `onDefaultNetworkChanged` callback when there is any changes in the + // transport types. + List currentTransportTypes = getTransportTypes(networkCapabilities); + if (!previousTransportTypes.equals(currentTransportTypes)) { onDefaultNetworkChanged(networkCapabilities); } } @@ -99,16 +101,17 @@ public void onCapabilitiesChanged(@NonNull Network network, @Override public void onLost(@NonNull Network network) { envoyEngine.onDefaultNetworkUnavailable(); - transportType = EMPTY_TRANSPORT_TYPE; + previousTransportTypes.clear(); } - private static int getTransportType(NetworkCapabilities networkCapabilities) { + private static List getTransportTypes(NetworkCapabilities networkCapabilities) { + List transportTypes = new ArrayList<>(); for (int type : TRANSPORT_TYPES) { if (networkCapabilities.hasTransport(type)) { - return type; + transportTypes.add(type); } } - return EMPTY_TRANSPORT_TYPE; + return transportTypes; } private void onDefaultNetworkChanged(NetworkCapabilities networkCapabilities) { @@ -122,7 +125,7 @@ private void onDefaultNetworkChanged(NetworkCapabilities networkCapabilities) { envoyEngine.onDefaultNetworkChanged(EnvoyNetworkType.GENERIC); } } - transportType = getTransportType(networkCapabilities); + previousTransportTypes = getTransportTypes(networkCapabilities); } } diff --git a/mobile/test/java/io/envoyproxy/envoymobile/engine/AndroidNetworkMonitorTest.java b/mobile/test/java/io/envoyproxy/envoymobile/engine/AndroidNetworkMonitorTest.java index bd433ce92269..5d1981233859 100644 --- a/mobile/test/java/io/envoyproxy/envoymobile/engine/AndroidNetworkMonitorTest.java +++ b/mobile/test/java/io/envoyproxy/envoymobile/engine/AndroidNetworkMonitorTest.java @@ -167,7 +167,7 @@ public void testOnCapabilitiesChangedPreviousNetworkIsEmptyCallbackIsCalledGener public void testOnCapabilitiesChangedPreviousNetworkNotEmptyCallbackIsCalledWwan() { AndroidNetworkMonitor.DefaultNetworkCallback callback = new AndroidNetworkMonitor.DefaultNetworkCallback(mockEnvoyEngine); - callback.transportType = NetworkCapabilities.TRANSPORT_WIFI; + callback.previousTransportTypes.add(NetworkCapabilities.TRANSPORT_WIFI); NetworkCapabilities capabilities = ShadowNetworkCapabilities.newInstance(); shadowOf(capabilities).addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); shadowOf(capabilities).addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); @@ -180,7 +180,7 @@ public void testOnCapabilitiesChangedPreviousNetworkNotEmptyCallbackIsCalledWwan public void testOnCapabilitiesChangedPreviousNetworkNotEmptyCallCallbackIsCalledWlan() { AndroidNetworkMonitor.DefaultNetworkCallback callback = new AndroidNetworkMonitor.DefaultNetworkCallback(mockEnvoyEngine); - callback.transportType = NetworkCapabilities.TRANSPORT_CELLULAR; + callback.previousTransportTypes.add(NetworkCapabilities.TRANSPORT_CELLULAR); NetworkCapabilities capabilities = ShadowNetworkCapabilities.newInstance(); shadowOf(capabilities).addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); shadowOf(capabilities).addTransportType(NetworkCapabilities.TRANSPORT_WIFI); @@ -193,7 +193,7 @@ public void testOnCapabilitiesChangedPreviousNetworkNotEmptyCallCallbackIsCalled public void testOnCapabilitiesChangedPreviousNetworkNotEmptyCallbackIsCalledGeneric() { AndroidNetworkMonitor.DefaultNetworkCallback callback = new AndroidNetworkMonitor.DefaultNetworkCallback(mockEnvoyEngine); - callback.transportType = NetworkCapabilities.TRANSPORT_BLUETOOTH; + callback.previousTransportTypes.add(NetworkCapabilities.TRANSPORT_BLUETOOTH); NetworkCapabilities capabilities = ShadowNetworkCapabilities.newInstance(); shadowOf(capabilities).addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); shadowOf(capabilities).addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET); @@ -202,11 +202,39 @@ public void testOnCapabilitiesChangedPreviousNetworkNotEmptyCallbackIsCalledGene verify(mockEnvoyEngine).onDefaultNetworkChanged(EnvoyNetworkType.GENERIC); } + @Test + public void testOnCapabilitiesChangedPreviousNetworkLessThanCurrentNetworkCallbackIsCalled() { + AndroidNetworkMonitor.DefaultNetworkCallback callback = + new AndroidNetworkMonitor.DefaultNetworkCallback(mockEnvoyEngine); + callback.previousTransportTypes.add(NetworkCapabilities.TRANSPORT_WIFI); + NetworkCapabilities capabilities = ShadowNetworkCapabilities.newInstance(); + shadowOf(capabilities).addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + shadowOf(capabilities).addTransportType(NetworkCapabilities.TRANSPORT_WIFI); + shadowOf(capabilities).addTransportType(NetworkCapabilities.TRANSPORT_VPN); + callback.onCapabilitiesChanged(ShadowNetwork.newInstance(0), capabilities); + + verify(mockEnvoyEngine).onDefaultNetworkChanged(EnvoyNetworkType.WLAN); + } + + @Test + public void testOnCapabilitiesChangedPreviousNetworkMoreThanCurrentNetworkCallbackIsCalled() { + AndroidNetworkMonitor.DefaultNetworkCallback callback = + new AndroidNetworkMonitor.DefaultNetworkCallback(mockEnvoyEngine); + callback.previousTransportTypes.add(NetworkCapabilities.TRANSPORT_WIFI); + callback.previousTransportTypes.add(NetworkCapabilities.TRANSPORT_VPN); + NetworkCapabilities capabilities = ShadowNetworkCapabilities.newInstance(); + shadowOf(capabilities).addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + shadowOf(capabilities).addTransportType(NetworkCapabilities.TRANSPORT_WIFI); + callback.onCapabilitiesChanged(ShadowNetwork.newInstance(0), capabilities); + + verify(mockEnvoyEngine).onDefaultNetworkChanged(EnvoyNetworkType.WLAN); + } + @Test public void testOnCapabilitiesChangedPreviousNetworkNotEmptyCallbackIsNotCalled() { AndroidNetworkMonitor.DefaultNetworkCallback callback = new AndroidNetworkMonitor.DefaultNetworkCallback(mockEnvoyEngine); - callback.transportType = NetworkCapabilities.TRANSPORT_WIFI; + callback.previousTransportTypes.add(NetworkCapabilities.TRANSPORT_WIFI); NetworkCapabilities capabilities = ShadowNetworkCapabilities.newInstance(); shadowOf(capabilities).addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); shadowOf(capabilities).addTransportType(NetworkCapabilities.TRANSPORT_WIFI);