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
This commit is contained in:
Lorenzo Colitti
2021-02-28 23:41:27 +09:00
parent b908e0c867
commit 57957fe8a1
2 changed files with 102 additions and 22 deletions

View File

@@ -16,19 +16,20 @@
package com.android.networkstack.tethering; package com.android.networkstack.tethering;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.IConnectivityManager; import android.net.IConnectivityManager;
import android.net.LinkProperties; import android.net.LinkProperties;
import android.net.Network; import android.net.Network;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest; import android.net.NetworkRequest;
import android.os.Handler; import android.os.Handler;
import android.os.UserHandle;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@@ -39,6 +40,10 @@ import java.util.Set;
/** /**
* Simulates upstream switching and sending NetworkCallbacks and CONNECTIVITY_ACTION broadcasts. * 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. * TODO: this duplicates a fair amount of code from ConnectivityManager and ConnectivityService.
* Consider using a ConnectivityService object instead, as used in ConnectivityServiceTest. * Consider using a ConnectivityService object instead, as used in ConnectivityServiceTest.
* *
@@ -63,11 +68,21 @@ public class TestConnectivityManager extends ConnectivityManager {
public Map<NetworkCallback, Integer> legacyTypeMap = new HashMap<>(); public Map<NetworkCallback, Integer> legacyTypeMap = new HashMap<>();
private final NetworkRequest mDefaultRequest; private final NetworkRequest mDefaultRequest;
private final Context mContext;
private int mNetworkId = 100; 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, public TestConnectivityManager(Context ctx, IConnectivityManager svc,
NetworkRequest defaultRequest) { NetworkRequest defaultRequest) {
super(ctx, svc); super(ctx, svc);
mContext = ctx;
mDefaultRequest = defaultRequest; mDefaultRequest = defaultRequest;
} }
@@ -109,6 +124,13 @@ public class TestConnectivityManager extends ConnectivityManager {
final TestNetworkAgent formerDefault = defaultNetwork; final TestNetworkAgent formerDefault = defaultNetwork;
defaultNetwork = agent; defaultNetwork = agent;
if (formerDefault != null) {
sendConnectivityAction(formerDefault.legacyType, false /* connected */);
}
if (defaultNetwork != null) {
sendConnectivityAction(defaultNetwork.legacyType, true /* connected */);
}
for (NetworkCallback cb : trackingDefault) { for (NetworkCallback cb : trackingDefault) {
if (defaultNetwork != null) { if (defaultNetwork != null) {
cb.onAvailable(defaultNetwork.networkId); cb.onAvailable(defaultNetwork.networkId);
@@ -194,24 +216,72 @@ public class TestConnectivityManager extends ConnectivityManager {
assertFalse(requested.containsKey(cb)); 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 static class TestNetworkAgent {
public final TestConnectivityManager cm; public final TestConnectivityManager cm;
public final Network networkId; public final Network networkId;
public final int transportType;
public final NetworkCapabilities networkCapabilities; public final NetworkCapabilities networkCapabilities;
public final LinkProperties linkProperties; 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.cm = cm;
this.networkId = new Network(cm.getNetworkId()); this.networkId = new Network(cm.getNetworkId());
this.transportType = transportType; networkCapabilities = copy(nc);
networkCapabilities = new NetworkCapabilities();
networkCapabilities.addTransportType(transportType);
networkCapabilities.addCapability(NET_CAPABILITY_INTERNET);
linkProperties = new LinkProperties(); 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() { 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()) { for (NetworkCallback cb : cm.listening.keySet()) {
cb.onAvailable(networkId); cb.onAvailable(networkId);
cb.onCapabilitiesChanged(networkId, copy(networkCapabilities)); cb.onCapabilitiesChanged(networkId, copy(networkCapabilities));
@@ -220,6 +290,12 @@ public class TestConnectivityManager extends ConnectivityManager {
} }
public void fakeDisconnect() { 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()) { for (NetworkCallback cb : cm.listening.keySet()) {
cb.onLost(networkId); cb.onLost(networkId);
} }

View File

@@ -85,6 +85,13 @@ public class UpstreamNetworkMonitorTest {
// any specific TRANSPORT_* is sufficient to identify this request. // any specific TRANSPORT_* is sufficient to identify this request.
private static final NetworkRequest sDefaultRequest = new NetworkRequest.Builder().build(); 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 Context mContext;
@Mock private EntitlementManager mEntitleMgr; @Mock private EntitlementManager mEntitleMgr;
@Mock private IConnectivityManager mCS; @Mock private IConnectivityManager mCS;
@@ -288,7 +295,7 @@ public class UpstreamNetworkMonitorTest {
// There are no networks, so there is nothing to select. // There are no networks, so there is nothing to select.
assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes)); assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI); final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, WIFI_CAPABILITIES);
wifiAgent.fakeConnect(); wifiAgent.fakeConnect();
// WiFi is up, we should prefer it. // WiFi is up, we should prefer it.
assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes)); assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
@@ -296,7 +303,7 @@ public class UpstreamNetworkMonitorTest {
// There are no networks, so there is nothing to select. // There are no networks, so there is nothing to select.
assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes)); assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, CELL_CAPABILITIES);
cellAgent.fakeConnect(); cellAgent.fakeConnect();
assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes)); assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
@@ -337,8 +344,7 @@ public class UpstreamNetworkMonitorTest {
mUNM.updateMobileRequiresDun(true); mUNM.updateMobileRequiresDun(true);
assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes)); assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, DUN_CAPABILITIES);
dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
dunAgent.fakeConnect(); dunAgent.fakeConnect();
// WiFi is still preferred. // WiFi is still preferred.
@@ -370,7 +376,7 @@ public class UpstreamNetworkMonitorTest {
mUNM.updateMobileRequiresDun(false); mUNM.updateMobileRequiresDun(false);
// [0] Mobile connects, DUN not required -> mobile selected. // [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(); cellAgent.fakeConnect();
mCM.makeDefaultNetwork(cellAgent); mCM.makeDefaultNetwork(cellAgent);
assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network); assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
@@ -381,7 +387,7 @@ public class UpstreamNetworkMonitorTest {
when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true); when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
// [2] WiFi connects but not validated/promoted to default -> mobile selected. // [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(); wifiAgent.fakeConnect();
assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network); assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
@@ -401,7 +407,7 @@ public class UpstreamNetworkMonitorTest {
// into UNM we should test for this here. // into UNM we should test for this here.
// [6] DUN network arrives -> DUN selected // [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.addCapability(NET_CAPABILITY_DUN);
dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET); dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
dunAgent.fakeConnect(); dunAgent.fakeConnect();
@@ -424,7 +430,7 @@ public class UpstreamNetworkMonitorTest {
final Set<String> alreadySeen = new HashSet<>(); final Set<String> alreadySeen = new HashSet<>();
// [1] Pretend Wi-Fi connects. // [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; final LinkProperties wifiLp = wifiAgent.linkProperties;
wifiLp.setInterfaceName("wlan0"); wifiLp.setInterfaceName("wlan0");
final String[] wifi_addrs = { final String[] wifi_addrs = {
@@ -451,7 +457,7 @@ public class UpstreamNetworkMonitorTest {
assertEquals(alreadySeen.size(), local.size()); assertEquals(alreadySeen.size(), local.size());
// [2] Pretend mobile connects. // [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; final LinkProperties cellLp = cellAgent.linkProperties;
cellLp.setInterfaceName("rmnet_data0"); cellLp.setInterfaceName("rmnet_data0");
final String[] cell_addrs = { final String[] cell_addrs = {
@@ -472,9 +478,7 @@ public class UpstreamNetworkMonitorTest {
assertEquals(alreadySeen.size(), local.size()); assertEquals(alreadySeen.size(), local.size());
// [3] Pretend DUN connects. // [3] Pretend DUN connects.
final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, DUN_CAPABILITIES);
dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
final LinkProperties dunLp = dunAgent.linkProperties; final LinkProperties dunLp = dunAgent.linkProperties;
dunLp.setInterfaceName("rmnet_data1"); dunLp.setInterfaceName("rmnet_data1");
final String[] dun_addrs = { final String[] dun_addrs = {
@@ -524,11 +528,11 @@ public class UpstreamNetworkMonitorTest {
mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr); mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
mUNM.startObserveAllNetworks(); mUNM.startObserveAllNetworks();
// Setup wifi and make wifi as default network. // 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(); wifiAgent.fakeConnect();
mCM.makeDefaultNetwork(wifiAgent); mCM.makeDefaultNetwork(wifiAgent);
// Setup mobile network. // Setup mobile network.
final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, CELL_CAPABILITIES);
cellAgent.fakeConnect(); cellAgent.fakeConnect();
assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI, assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI,