From 57957fe8a1fcfb59f3737653f1213de2ec1880fa Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Sun, 28 Feb 2021 23:41:27 +0900 Subject: [PATCH] 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 --- .../tethering/TestConnectivityManager.java | 92 +++++++++++++++++-- .../tethering/UpstreamNetworkMonitorTest.java | 32 ++++--- 2 files changed, 102 insertions(+), 22 deletions(-) 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 3a6350c026..3636b0382c 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 @@ import java.util.Set; /** * 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 class TestConnectivityManager extends ConnectivityManager { public Map 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 @@ public class TestConnectivityManager extends ConnectivityManager { 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 @@ public class TestConnectivityManager extends ConnectivityManager { 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 class TestConnectivityManager extends ConnectivityManager { } 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 e358f5a2cd..7d735fc48f 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 @@ public class UpstreamNetworkMonitorTest { // 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 @@ public class UpstreamNetworkMonitorTest { // 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 @@ public class UpstreamNetworkMonitorTest { // 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 @@ public class UpstreamNetworkMonitorTest { 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 @@ public class UpstreamNetworkMonitorTest { 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 @@ public class UpstreamNetworkMonitorTest { 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 @@ public class UpstreamNetworkMonitorTest { // 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 @@ public class UpstreamNetworkMonitorTest { final Set 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 @@ public class UpstreamNetworkMonitorTest { 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 @@ public class UpstreamNetworkMonitorTest { 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 @@ public class UpstreamNetworkMonitorTest { 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,