Make TestConnectivityManager send CONNECTIVITY_ACTION.
The tethering code still depends on CONNECTIVITY_ACTION for
upstream selection. Make TestConnectivityManager send these
broadcasts.
Bug: 173068192
Test: atest TetheringTests
Change-Id: I6a32e99fafef9d6d2abec438ffc68164ab4c5bdf
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java
index 3a6350c..3636b03 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java
@@ -16,19 +16,20 @@
package com.android.networkstack.tethering;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
-
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
import android.content.Context;
+import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
import android.net.NetworkRequest;
import android.os.Handler;
+import android.os.UserHandle;
import java.util.HashMap;
import java.util.HashSet;
@@ -39,6 +40,10 @@
/**
* Simulates upstream switching and sending NetworkCallbacks and CONNECTIVITY_ACTION broadcasts.
*
+ * Unlike any real networking code, this class is single-threaded and entirely synchronous.
+ * The effects of all method calls (including sending fake broadcasts, sending callbacks, etc.) are
+ * performed immediately on the caller's thread before returning.
+ *
* TODO: this duplicates a fair amount of code from ConnectivityManager and ConnectivityService.
* Consider using a ConnectivityService object instead, as used in ConnectivityServiceTest.
*
@@ -63,11 +68,21 @@
public Map<NetworkCallback, Integer> legacyTypeMap = new HashMap<>();
private final NetworkRequest mDefaultRequest;
+ private final Context mContext;
+
private int mNetworkId = 100;
+ /**
+ * Constructs a TestConnectivityManager.
+ * @param ctx the context to use. Must be a fake or a mock because otherwise the test will
+ * attempt to send real broadcasts and resulting in permission denials.
+ * @param svc an IConnectivityManager. Should be a fake or a mock.
+ * @param defaultRequest the default NetworkRequest that will be used by Tethering.
+ */
public TestConnectivityManager(Context ctx, IConnectivityManager svc,
NetworkRequest defaultRequest) {
super(ctx, svc);
+ mContext = ctx;
mDefaultRequest = defaultRequest;
}
@@ -109,6 +124,13 @@
final TestNetworkAgent formerDefault = defaultNetwork;
defaultNetwork = agent;
+ if (formerDefault != null) {
+ sendConnectivityAction(formerDefault.legacyType, false /* connected */);
+ }
+ if (defaultNetwork != null) {
+ sendConnectivityAction(defaultNetwork.legacyType, true /* connected */);
+ }
+
for (NetworkCallback cb : trackingDefault) {
if (defaultNetwork != null) {
cb.onAvailable(defaultNetwork.networkId);
@@ -194,24 +216,72 @@
assertFalse(requested.containsKey(cb));
}
+ private void sendConnectivityAction(int type, boolean connected) {
+ NetworkInfo ni = new NetworkInfo(type, 0 /* subtype */, getNetworkTypeName(type),
+ "" /* subtypeName */);
+ NetworkInfo.DetailedState state = connected
+ ? NetworkInfo.DetailedState.CONNECTED
+ : NetworkInfo.DetailedState.DISCONNECTED;
+ ni.setDetailedState(state, "" /* reason */, "" /* extraInfo */);
+ Intent intent = new Intent(CONNECTIVITY_ACTION);
+ intent.putExtra(EXTRA_NETWORK_INFO, ni);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
public static class TestNetworkAgent {
public final TestConnectivityManager cm;
public final Network networkId;
- public final int transportType;
public final NetworkCapabilities networkCapabilities;
public final LinkProperties linkProperties;
+ // TODO: delete when tethering no longer uses CONNECTIVITY_ACTION.
+ public final int legacyType;
- public TestNetworkAgent(TestConnectivityManager cm, int transportType) {
+ public TestNetworkAgent(TestConnectivityManager cm, NetworkCapabilities nc) {
this.cm = cm;
this.networkId = new Network(cm.getNetworkId());
- this.transportType = transportType;
- networkCapabilities = new NetworkCapabilities();
- networkCapabilities.addTransportType(transportType);
- networkCapabilities.addCapability(NET_CAPABILITY_INTERNET);
+ networkCapabilities = copy(nc);
linkProperties = new LinkProperties();
+ legacyType = toLegacyType(nc);
+ }
+
+ public TestNetworkAgent(TestConnectivityManager cm, UpstreamNetworkState state) {
+ this.cm = cm;
+ networkId = state.network;
+ networkCapabilities = state.networkCapabilities;
+ linkProperties = state.linkProperties;
+ this.legacyType = toLegacyType(networkCapabilities);
+ }
+
+ private static int toLegacyType(NetworkCapabilities nc) {
+ for (int type = 0; type < ConnectivityManager.TYPE_TEST; type++) {
+ if (matchesLegacyType(nc, type)) return type;
+ }
+ throw new IllegalArgumentException(("Can't determine legacy type for: ") + nc);
+ }
+
+ private static boolean matchesLegacyType(NetworkCapabilities nc, int legacyType) {
+ final NetworkCapabilities typeNc;
+ try {
+ typeNc = ConnectivityManager.networkCapabilitiesForType(legacyType);
+ } catch (IllegalArgumentException e) {
+ // networkCapabilitiesForType does not support all legacy types.
+ return false;
+ }
+ return typeNc.satisfiedByNetworkCapabilities(nc);
+ }
+
+ private boolean matchesLegacyType(int legacyType) {
+ return matchesLegacyType(networkCapabilities, legacyType);
}
public void fakeConnect() {
+ for (NetworkRequest request : cm.requested.values()) {
+ if (matchesLegacyType(request.legacyType)) {
+ cm.sendConnectivityAction(legacyType, true /* connected */);
+ // In practice, a given network can match only one legacy type.
+ break;
+ }
+ }
for (NetworkCallback cb : cm.listening.keySet()) {
cb.onAvailable(networkId);
cb.onCapabilitiesChanged(networkId, copy(networkCapabilities));
@@ -220,6 +290,12 @@
}
public void fakeDisconnect() {
+ for (NetworkRequest request : cm.requested.values()) {
+ if (matchesLegacyType(request.legacyType)) {
+ cm.sendConnectivityAction(legacyType, false /* connected */);
+ break;
+ }
+ }
for (NetworkCallback cb : cm.listening.keySet()) {
cb.onLost(networkId);
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
index e358f5a..7d735fc 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
@@ -85,6 +85,13 @@
// any specific TRANSPORT_* is sufficient to identify this request.
private static final NetworkRequest sDefaultRequest = new NetworkRequest.Builder().build();
+ private static final NetworkCapabilities CELL_CAPABILITIES = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).addCapability(NET_CAPABILITY_INTERNET).build();
+ private static final NetworkCapabilities DUN_CAPABILITIES = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).addCapability(NET_CAPABILITY_DUN).build();
+ private static final NetworkCapabilities WIFI_CAPABILITIES = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_WIFI).addCapability(NET_CAPABILITY_INTERNET).build();
+
@Mock private Context mContext;
@Mock private EntitlementManager mEntitleMgr;
@Mock private IConnectivityManager mCS;
@@ -288,7 +295,7 @@
// There are no networks, so there is nothing to select.
assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
- final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
+ final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, WIFI_CAPABILITIES);
wifiAgent.fakeConnect();
// WiFi is up, we should prefer it.
assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
@@ -296,7 +303,7 @@
// There are no networks, so there is nothing to select.
assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
- final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
+ final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, CELL_CAPABILITIES);
cellAgent.fakeConnect();
assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
@@ -337,8 +344,7 @@
mUNM.updateMobileRequiresDun(true);
assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
- final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
- dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
+ final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, DUN_CAPABILITIES);
dunAgent.fakeConnect();
// WiFi is still preferred.
@@ -370,7 +376,7 @@
mUNM.updateMobileRequiresDun(false);
// [0] Mobile connects, DUN not required -> mobile selected.
- final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
+ final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, CELL_CAPABILITIES);
cellAgent.fakeConnect();
mCM.makeDefaultNetwork(cellAgent);
assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
@@ -381,7 +387,7 @@
when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
// [2] WiFi connects but not validated/promoted to default -> mobile selected.
- final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
+ final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, WIFI_CAPABILITIES);
wifiAgent.fakeConnect();
assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
@@ -401,7 +407,7 @@
// into UNM we should test for this here.
// [6] DUN network arrives -> DUN selected
- final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
+ final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, CELL_CAPABILITIES);
dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
dunAgent.fakeConnect();
@@ -424,7 +430,7 @@
final Set<String> alreadySeen = new HashSet<>();
// [1] Pretend Wi-Fi connects.
- final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
+ final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, WIFI_CAPABILITIES);
final LinkProperties wifiLp = wifiAgent.linkProperties;
wifiLp.setInterfaceName("wlan0");
final String[] wifi_addrs = {
@@ -451,7 +457,7 @@
assertEquals(alreadySeen.size(), local.size());
// [2] Pretend mobile connects.
- final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
+ final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, CELL_CAPABILITIES);
final LinkProperties cellLp = cellAgent.linkProperties;
cellLp.setInterfaceName("rmnet_data0");
final String[] cell_addrs = {
@@ -472,9 +478,7 @@
assertEquals(alreadySeen.size(), local.size());
// [3] Pretend DUN connects.
- final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
- dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
- dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
+ final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, DUN_CAPABILITIES);
final LinkProperties dunLp = dunAgent.linkProperties;
dunLp.setInterfaceName("rmnet_data1");
final String[] dun_addrs = {
@@ -524,11 +528,11 @@
mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
mUNM.startObserveAllNetworks();
// Setup wifi and make wifi as default network.
- final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
+ final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, WIFI_CAPABILITIES);
wifiAgent.fakeConnect();
mCM.makeDefaultNetwork(wifiAgent);
// Setup mobile network.
- final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
+ final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, CELL_CAPABILITIES);
cellAgent.fakeConnect();
assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI,