Skip to content

Commit

Permalink
mobile: Support for multiple transport types on the Android network m…
Browse files Browse the repository at this point in the history
…onitor (#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 <[email protected]>
  • Loading branch information
fredyw authored Nov 22, 2024
1 parent d7099b6 commit 9aad332
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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<Integer> previousTransportTypes = new ArrayList<>();

DefaultNetworkCallback(EnvoyEngine envoyEngine) { this.envoyEngine = envoyEngine; }

Expand All @@ -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<Integer> currentTransportTypes = getTransportTypes(networkCapabilities);
if (!previousTransportTypes.equals(currentTransportTypes)) {
onDefaultNetworkChanged(networkCapabilities);
}
}
Expand All @@ -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<Integer> getTransportTypes(NetworkCapabilities networkCapabilities) {
List<Integer> 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) {
Expand All @@ -122,7 +125,7 @@ private void onDefaultNetworkChanged(NetworkCapabilities networkCapabilities) {
envoyEngine.onDefaultNetworkChanged(EnvoyNetworkType.GENERIC);
}
}
transportType = getTransportType(networkCapabilities);
previousTransportTypes = getTransportTypes(networkCapabilities);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down

0 comments on commit 9aad332

Please sign in to comment.