From 3e09f80e8a43c7cfd7d44b398f807cee947a283f Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 15 Mar 2021 13:00:58 +0900 Subject: [PATCH] Add the ability to affect ordering in the tethering unit tests. Currently, TestConnectivityManager immediately sends all callbacks and broadcasts to the Tethering code as soon as the test code makes any change. This makes it impossible to affect the order in which those events are delivered to the Tethering code, so it is not possible to test for races. Fix some of this as follows: 1. Make TestConnectivityManager post all its callbacks to the handlers that Tethering registered them with. 2. In TetheringTest, use the existing TestLooper object to advance time manually. Also use setUseRegisteredHandlers to ensure that the broadcasts are sent in order. This requires calling dispatchAll() after sending the broadcast to preserve the existing synchronous behaviour. Take advantage of that to remove lots of existing dispatchAll calls. 3. Add a TestLooper to UpstreamNetworkMonitorTest and use it. Keep the test passing by adding lots of mLooper.dispatchAll(), which is a bit ugly but probably acceptable given the additional coverage it provides. This exposes an existing bug in the code where if upstream selection is in automatic mode, and all CONNECTIVITY_ACTION broadcasts are received before all NetworkCallbacks, the code does not switch upstream. In order to make the tests pass, re-order the CONNECTIVITY_ACTION broadcasts with the NetworkCallbacks in TestConnectivityManager so as not to trigger the bug. A future CL will make the order configurable. While I'm at it, switch TestConnectivityManager from HashMap to ArrayMap, which is generally preferred for maps that do not contain too many elements. Bug: 173068192 Test: test-only change Change-Id: I964f365c691fbc396ab0a87f292bd32b123011fe --- .../tethering/TestConnectivityManager.java | 99 +++++++++++-------- .../networkstack/tethering/TetheringTest.java | 27 ++--- .../tethering/UpstreamNetworkMonitorTest.java | 37 +++++-- 3 files changed, 96 insertions(+), 67 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 3636b0382c..7ac80b06a6 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java @@ -30,12 +30,10 @@ import android.net.NetworkInfo; import android.net.NetworkRequest; import android.os.Handler; import android.os.UserHandle; +import android.util.ArrayMap; -import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Objects; -import java.util.Set; /** * Simulates upstream switching and sending NetworkCallbacks and CONNECTIVITY_ACTION broadcasts. @@ -60,12 +58,12 @@ import java.util.Set; * that state changes), this may become less important or unnecessary. */ public class TestConnectivityManager extends ConnectivityManager { - public Map allCallbacks = new HashMap<>(); - public Set trackingDefault = new HashSet<>(); + public Map allCallbacks = new ArrayMap<>(); + public Map trackingDefault = new ArrayMap<>(); public TestNetworkAgent defaultNetwork = null; - public Map listening = new HashMap<>(); - public Map requested = new HashMap<>(); - public Map legacyTypeMap = new HashMap<>(); + public Map listening = new ArrayMap<>(); + public Map requested = new ArrayMap<>(); + public Map legacyTypeMap = new ArrayMap<>(); private final NetworkRequest mDefaultRequest; private final Context mContext; @@ -86,6 +84,15 @@ public class TestConnectivityManager extends ConnectivityManager { mDefaultRequest = defaultRequest; } + class NetworkRequestInfo { + public final NetworkRequest request; + public final Handler handler; + NetworkRequestInfo(NetworkRequest r, Handler h) { + request = r; + handler = h; + } + } + boolean hasNoCallbacks() { return allCallbacks.isEmpty() && trackingDefault.isEmpty() @@ -106,8 +113,8 @@ public class TestConnectivityManager extends ConnectivityManager { final NetworkCapabilities empty = new NetworkCapabilities(); empty.clearAll(); - for (NetworkRequest req : listening.values()) { - if (req.networkCapabilities.equalRequestableCapabilities(empty)) { + for (NetworkRequestInfo nri : listening.values()) { + if (nri.request.networkCapabilities.equalRequestableCapabilities(empty)) { return true; } } @@ -118,40 +125,52 @@ public class TestConnectivityManager extends ConnectivityManager { return ++mNetworkId; } - void makeDefaultNetwork(TestNetworkAgent agent) { - if (Objects.equals(defaultNetwork, agent)) return; - - final TestNetworkAgent formerDefault = defaultNetwork; - defaultNetwork = agent; - + private void sendDefaultNetworkBroadcasts(TestNetworkAgent formerDefault, + TestNetworkAgent defaultNetwork) { if (formerDefault != null) { sendConnectivityAction(formerDefault.legacyType, false /* connected */); } if (defaultNetwork != null) { sendConnectivityAction(defaultNetwork.legacyType, true /* connected */); } + } - for (NetworkCallback cb : trackingDefault) { + private void sendDefaultNetworkCallbacks(TestNetworkAgent formerDefault, + TestNetworkAgent defaultNetwork) { + for (NetworkCallback cb : trackingDefault.keySet()) { + final NetworkRequestInfo nri = trackingDefault.get(cb); if (defaultNetwork != null) { - cb.onAvailable(defaultNetwork.networkId); - cb.onCapabilitiesChanged( - defaultNetwork.networkId, defaultNetwork.networkCapabilities); - cb.onLinkPropertiesChanged( - defaultNetwork.networkId, defaultNetwork.linkProperties); + nri.handler.post(() -> cb.onAvailable(defaultNetwork.networkId)); + nri.handler.post(() -> cb.onCapabilitiesChanged( + defaultNetwork.networkId, defaultNetwork.networkCapabilities)); + nri.handler.post(() -> cb.onLinkPropertiesChanged( + defaultNetwork.networkId, defaultNetwork.linkProperties)); + } else if (formerDefault != null) { + nri.handler.post(() -> cb.onLost(formerDefault.networkId)); } } } + void makeDefaultNetwork(TestNetworkAgent agent) { + if (Objects.equals(defaultNetwork, agent)) return; + + final TestNetworkAgent formerDefault = defaultNetwork; + defaultNetwork = agent; + + sendDefaultNetworkCallbacks(formerDefault, defaultNetwork); + sendDefaultNetworkBroadcasts(formerDefault, defaultNetwork); + } + @Override public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) { assertFalse(allCallbacks.containsKey(cb)); - allCallbacks.put(cb, h); + allCallbacks.put(cb, new NetworkRequestInfo(req, h)); if (mDefaultRequest.equals(req)) { - assertFalse(trackingDefault.contains(cb)); - trackingDefault.add(cb); + assertFalse(trackingDefault.containsKey(cb)); + trackingDefault.put(cb, new NetworkRequestInfo(req, h)); } else { assertFalse(requested.containsKey(cb)); - requested.put(cb, req); + requested.put(cb, new NetworkRequestInfo(req, h)); } } @@ -164,9 +183,9 @@ public class TestConnectivityManager extends ConnectivityManager { public void requestNetwork(NetworkRequest req, int timeoutMs, int legacyType, Handler h, NetworkCallback cb) { assertFalse(allCallbacks.containsKey(cb)); - allCallbacks.put(cb, h); + allCallbacks.put(cb, new NetworkRequestInfo(req, h)); assertFalse(requested.containsKey(cb)); - requested.put(cb, req); + requested.put(cb, new NetworkRequestInfo(req, h)); assertFalse(legacyTypeMap.containsKey(cb)); if (legacyType != ConnectivityManager.TYPE_NONE) { legacyTypeMap.put(cb, legacyType); @@ -176,9 +195,9 @@ public class TestConnectivityManager extends ConnectivityManager { @Override public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb, Handler h) { assertFalse(allCallbacks.containsKey(cb)); - allCallbacks.put(cb, h); + allCallbacks.put(cb, new NetworkRequestInfo(req, h)); assertFalse(listening.containsKey(cb)); - listening.put(cb, req); + listening.put(cb, new NetworkRequestInfo(req, h)); } @Override @@ -198,7 +217,7 @@ public class TestConnectivityManager extends ConnectivityManager { @Override public void unregisterNetworkCallback(NetworkCallback cb) { - if (trackingDefault.contains(cb)) { + if (trackingDefault.containsKey(cb)) { trackingDefault.remove(cb); } else if (listening.containsKey(cb)) { listening.remove(cb); @@ -211,7 +230,7 @@ public class TestConnectivityManager extends ConnectivityManager { allCallbacks.remove(cb); assertFalse(allCallbacks.containsKey(cb)); - assertFalse(trackingDefault.contains(cb)); + assertFalse(trackingDefault.containsKey(cb)); assertFalse(listening.containsKey(cb)); assertFalse(requested.containsKey(cb)); } @@ -275,23 +294,25 @@ public class TestConnectivityManager extends ConnectivityManager { } public void fakeConnect() { - for (NetworkRequest request : cm.requested.values()) { - if (matchesLegacyType(request.legacyType)) { + for (NetworkRequestInfo nri : cm.requested.values()) { + if (matchesLegacyType(nri.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)); - cb.onLinkPropertiesChanged(networkId, copy(linkProperties)); + final NetworkRequestInfo nri = cm.listening.get(cb); + nri.handler.post(() -> cb.onAvailable(networkId)); + nri.handler.post(() -> cb.onCapabilitiesChanged( + networkId, copy(networkCapabilities))); + nri.handler.post(() -> cb.onLinkPropertiesChanged(networkId, copy(linkProperties))); } } public void fakeDisconnect() { - for (NetworkRequest request : cm.requested.values()) { - if (matchesLegacyType(request.legacyType)) { + for (NetworkRequestInfo nri : cm.requested.values()) { + if (matchesLegacyType(nri.request.legacyType)) { cm.sendConnectivityAction(legacyType, false /* connected */); break; } diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java index d5c7add31c..a7d61bd341 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -618,6 +618,7 @@ public class TetheringTest { when(mOffloadHardwareInterface.getForwardedStats(any())).thenReturn(mForwardedStats); mServiceContext = new TestContext(mContext); + mServiceContext.setUseRegisteredHandlers(true); mContentResolver = new MockContentResolver(mServiceContext); mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); setTetheringSupported(true /* supported */); @@ -716,6 +717,7 @@ public class TetheringTest { final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); intent.putExtra(EXTRA_WIFI_AP_STATE, state); mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + mLooper.dispatchAll(); } private void sendWifiApStateChanged(int state, String ifname, int ipmode) { @@ -724,6 +726,7 @@ public class TetheringTest { intent.putExtra(EXTRA_WIFI_AP_INTERFACE_NAME, ifname); intent.putExtra(EXTRA_WIFI_AP_MODE, ipmode); mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + mLooper.dispatchAll(); } private static final String[] P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST = { @@ -750,6 +753,7 @@ public class TetheringTest { mServiceContext.sendBroadcastAsUserMultiplePermissions(intent, UserHandle.ALL, P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST); + mLooper.dispatchAll(); } private void sendUsbBroadcast(boolean connected, boolean configured, boolean function, @@ -763,11 +767,13 @@ public class TetheringTest { intent.putExtra(USB_FUNCTION_NCM, function); } mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + mLooper.dispatchAll(); } private void sendConfigurationChanged() { final Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED); mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + mLooper.dispatchAll(); } private void verifyDefaultNetworkRequestFiled() { @@ -809,7 +815,6 @@ public class TetheringTest { mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); } sendWifiApStateChanged(WIFI_AP_STATE_ENABLED); - mLooper.dispatchAll(); // If, and only if, Tethering received an interface status changed then // it creates a IpServer and sends out a broadcast indicating that the @@ -857,7 +862,6 @@ public class TetheringTest { // Pretend we then receive USB configured broadcast. sendUsbBroadcast(true, true, true, TETHERING_USB); - mLooper.dispatchAll(); // Now we should see the start of tethering mechanics (in this case: // tetherMatchingInterfaces() which starts by fetching all interfaces). verify(mNetd, times(1)).interfaceGetList(); @@ -886,7 +890,6 @@ public class TetheringTest { mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); } sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_LOCAL_ONLY); - mLooper.dispatchAll(); verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME); verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); @@ -948,7 +951,6 @@ public class TetheringTest { initTetheringUpstream(upstreamState); prepareUsbTethering(); sendUsbBroadcast(true, true, true, TETHERING_USB); - mLooper.dispatchAll(); } private void assertSetIfaceToDadProxy(final int numOfCalls, final String ifaceName) { @@ -1099,7 +1101,6 @@ public class TetheringTest { // Start USB tethering with no current upstream. prepareUsbTethering(); sendUsbBroadcast(true, true, true, TETHERING_USB); - mLooper.dispatchAll(); inOrder.verify(mUpstreamNetworkMonitor).startObserveAllNetworks(); inOrder.verify(mUpstreamNetworkMonitor).setTryCell(true); @@ -1157,7 +1158,6 @@ public class TetheringTest { private void runNcmTethering() { prepareNcmTethering(); sendUsbBroadcast(true, true, true, TETHERING_NCM); - mLooper.dispatchAll(); } @Test @@ -1205,7 +1205,6 @@ public class TetheringTest { // tethering mode is to be started. mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); sendWifiApStateChanged(WIFI_AP_STATE_ENABLED); - mLooper.dispatchAll(); // There is 1 IpServer state change event: STATE_AVAILABLE verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE); @@ -1233,7 +1232,6 @@ public class TetheringTest { // tethering mode is to be started. mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); - mLooper.dispatchAll(); verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME); verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); @@ -1310,7 +1308,6 @@ public class TetheringTest { // tethering mode is to be started. mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); - mLooper.dispatchAll(); // We verify get/set called three times here: twice for setup and once during // teardown because all events happen over the course of the single @@ -1634,7 +1631,6 @@ public class TetheringTest { mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null); sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); - mLooper.dispatchAll(); tetherState = callback.pollTetherStatesChanged(); assertArrayEquals(tetherState.tetheredList, new String[] {TEST_WLAN_IFNAME}); callback.expectUpstreamChanged(upstreamState.network); @@ -1656,7 +1652,6 @@ public class TetheringTest { mLooper.dispatchAll(); mTethering.stopTethering(TETHERING_WIFI); sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED); - mLooper.dispatchAll(); tetherState = callback2.pollTetherStatesChanged(); assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME}); mLooper.dispatchAll(); @@ -1749,7 +1744,6 @@ public class TetheringTest { mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true); } sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME); - mLooper.dispatchAll(); verifyInterfaceServingModeStarted(TEST_P2P_IFNAME); verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_AVAILABLE_TETHER); @@ -1767,7 +1761,6 @@ public class TetheringTest { // is being removed. sendWifiP2pConnectionChanged(false, true, TEST_P2P_IFNAME); mTethering.interfaceRemoved(TEST_P2P_IFNAME); - mLooper.dispatchAll(); verify(mNetd, times(1)).tetherApplyDnsInterfaces(); verify(mNetd, times(1)).tetherInterfaceRemove(TEST_P2P_IFNAME); @@ -1790,7 +1783,6 @@ public class TetheringTest { mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true); } sendWifiP2pConnectionChanged(true, false, TEST_P2P_IFNAME); - mLooper.dispatchAll(); verify(mNetd, never()).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); verify(mNetd, never()).tetherInterfaceAdd(TEST_P2P_IFNAME); @@ -1802,7 +1794,6 @@ public class TetheringTest { // is being removed. sendWifiP2pConnectionChanged(false, false, TEST_P2P_IFNAME); mTethering.interfaceRemoved(TEST_P2P_IFNAME); - mLooper.dispatchAll(); verify(mNetd, never()).tetherApplyDnsInterfaces(); verify(mNetd, never()).tetherInterfaceRemove(TEST_P2P_IFNAME); @@ -1838,7 +1829,6 @@ public class TetheringTest { mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true); } sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME); - mLooper.dispatchAll(); verify(mNetd, never()).interfaceSetCfg(any(InterfaceConfigurationParcel.class)); verify(mNetd, never()).tetherInterfaceAdd(TEST_P2P_IFNAME); @@ -1968,7 +1958,6 @@ public class TetheringTest { // Expect that when USB comes up, the DHCP server is configured with the requested address. mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true); sendUsbBroadcast(true, true, true, TETHERING_USB); - mLooper.dispatchAll(); verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( any(), any()); verify(mNetd).interfaceSetCfg(argThat(cfg -> serverAddr.equals(cfg.ipv4Addr))); @@ -1988,7 +1977,6 @@ public class TetheringTest { verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true); sendUsbBroadcast(true, true, true, TETHERING_USB); - mLooper.dispatchAll(); verify(mNetd).interfaceSetCfg(argThat(cfg -> serverAddr.equals(cfg.ipv4Addr))); verify(mIpServerDependencies, times(1)).makeDhcpServer(any(), dhcpParamsCaptor.capture(), any()); @@ -2212,7 +2200,6 @@ public class TetheringTest { mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true); sendUsbBroadcast(true, true, true, TETHERING_USB); - mLooper.dispatchAll(); assertContains(Arrays.asList(mTethering.getTetherableIfaces()), TEST_USB_IFNAME); assertContains(Arrays.asList(mTethering.getTetherableIfaces()), TEST_ETH_IFNAME); assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastTetherError(TEST_USB_IFNAME)); @@ -2251,7 +2238,6 @@ public class TetheringTest { // Run local only tethering. mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true); sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME); - mLooper.dispatchAll(); verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS)).startWithCallbacks( any(), dhcpEventCbsCaptor.capture()); eventCallbacks = dhcpEventCbsCaptor.getValue(); @@ -2268,7 +2254,6 @@ public class TetheringTest { // Run wifi tethering. mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); - mLooper.dispatchAll(); verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS)).startWithCallbacks( any(), dhcpEventCbsCaptor.capture()); eventCallbacks = dhcpEventCbsCaptor.getValue(); 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 43d32b5e36..40e4f83c19 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java @@ -50,13 +50,16 @@ import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.util.SharedLog; import android.os.Handler; +import android.os.Looper; import android.os.Message; +import android.os.test.TestLooper; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.State; import com.android.internal.util.StateMachine; +import com.android.networkstack.tethering.TestConnectivityManager.NetworkRequestInfo; import com.android.networkstack.tethering.TestConnectivityManager.TestNetworkAgent; import org.junit.After; @@ -100,6 +103,8 @@ public class UpstreamNetworkMonitorTest { private TestConnectivityManager mCM; private UpstreamNetworkMonitor mUNM; + private final TestLooper mLooper = new TestLooper(); + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); reset(mContext); @@ -109,7 +114,7 @@ public class UpstreamNetworkMonitorTest { when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true); mCM = spy(new TestConnectivityManager(mContext, mCS, sDefaultRequest)); - mSM = new TestStateMachine(); + mSM = new TestStateMachine(mLooper.getLooper()); mUNM = new UpstreamNetworkMonitor(mCM, mSM, mLog, EVENT_UNM_UPDATE); } @@ -295,14 +300,17 @@ public class UpstreamNetworkMonitorTest { final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, WIFI_CAPABILITIES); wifiAgent.fakeConnect(); + mLooper.dispatchAll(); // WiFi is up, we should prefer it. assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes)); wifiAgent.fakeDisconnect(); + mLooper.dispatchAll(); // There are no networks, so there is nothing to select. assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes)); final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, CELL_CAPABILITIES); cellAgent.fakeConnect(); + mLooper.dispatchAll(); assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes)); preferredTypes.add(TYPE_MOBILE_DUN); @@ -319,7 +327,7 @@ public class UpstreamNetworkMonitorTest { mUNM.selectPreferredUpstreamType(preferredTypes)); // Check to see we filed an explicit request. assertEquals(1, mCM.requested.size()); - NetworkRequest netReq = (NetworkRequest) mCM.requested.values().toArray()[0]; + NetworkRequest netReq = ((NetworkRequestInfo) mCM.requested.values().toArray()[0]).request; assertTrue(netReq.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)); assertFalse(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)); // mobile is not permitted, we should not use HIPRI. @@ -332,6 +340,7 @@ public class UpstreamNetworkMonitorTest { mUNM.selectPreferredUpstreamType(preferredTypes)); wifiAgent.fakeConnect(); + mLooper.dispatchAll(); // WiFi is up, and we should prefer it over cell. assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes)); assertEquals(0, mCM.requested.size()); @@ -344,17 +353,19 @@ public class UpstreamNetworkMonitorTest { final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, DUN_CAPABILITIES); dunAgent.fakeConnect(); + mLooper.dispatchAll(); // WiFi is still preferred. assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes)); // WiFi goes down, cell and DUN are still up but only DUN is preferred. wifiAgent.fakeDisconnect(); + mLooper.dispatchAll(); assertSatisfiesLegacyType(TYPE_MOBILE_DUN, mUNM.selectPreferredUpstreamType(preferredTypes)); // Check to see we filed an explicit request. assertEquals(1, mCM.requested.size()); - netReq = (NetworkRequest) mCM.requested.values().toArray()[0]; + netReq = ((NetworkRequestInfo) mCM.requested.values().toArray()[0]).request; assertTrue(netReq.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)); assertTrue(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)); // mobile is not permitted, we should not use DUN. @@ -378,6 +389,7 @@ public class UpstreamNetworkMonitorTest { final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, CELL_CAPABILITIES); cellAgent.fakeConnect(); mCM.makeDefaultNetwork(cellAgent); + mLooper.dispatchAll(); assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network); assertEquals(0, mCM.requested.size()); @@ -390,11 +402,13 @@ public class UpstreamNetworkMonitorTest { // [2] WiFi connects but not validated/promoted to default -> mobile selected. final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, WIFI_CAPABILITIES); wifiAgent.fakeConnect(); + mLooper.dispatchAll(); assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network); assertEquals(0, mCM.requested.size()); // [3] WiFi validates and is promoted to the default network -> WiFi selected. mCM.makeDefaultNetwork(wifiAgent); + mLooper.dispatchAll(); assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network); assertEquals(0, mCM.requested.size()); @@ -406,6 +420,7 @@ public class UpstreamNetworkMonitorTest { // [5] WiFi no longer validated, mobile becomes default, DUN required -> null selected. mCM.makeDefaultNetwork(cellAgent); + mLooper.dispatchAll(); assertEquals(null, mUNM.getCurrentPreferredUpstream()); assertEquals(1, mCM.requested.size()); assertTrue(isDunRequested()); @@ -415,6 +430,7 @@ public class UpstreamNetworkMonitorTest { dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN); dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET); dunAgent.fakeConnect(); + mLooper.dispatchAll(); assertEquals(dunAgent.networkId, mUNM.getCurrentPreferredUpstream().network); assertEquals(1, mCM.requested.size()); @@ -461,6 +477,7 @@ public class UpstreamNetworkMonitorTest { } wifiAgent.fakeConnect(); wifiAgent.sendLinkProperties(); + mLooper.dispatchAll(); local = mUNM.getLocalPrefixes(); assertPrefixSet(local, INCLUDES, alreadySeen); @@ -485,6 +502,7 @@ public class UpstreamNetworkMonitorTest { } cellAgent.fakeConnect(); cellAgent.sendLinkProperties(); + mLooper.dispatchAll(); local = mUNM.getLocalPrefixes(); assertPrefixSet(local, INCLUDES, alreadySeen); @@ -506,6 +524,7 @@ public class UpstreamNetworkMonitorTest { } dunAgent.fakeConnect(); dunAgent.sendLinkProperties(); + mLooper.dispatchAll(); local = mUNM.getLocalPrefixes(); assertPrefixSet(local, INCLUDES, alreadySeen); @@ -517,6 +536,7 @@ public class UpstreamNetworkMonitorTest { // [4] Pretend Wi-Fi disconnected. It's addresses/prefixes should no // longer be included (should be properly removed). wifiAgent.fakeDisconnect(); + mLooper.dispatchAll(); local = mUNM.getLocalPrefixes(); assertPrefixSet(local, EXCLUDES, wifiLinkPrefixes); assertPrefixSet(local, INCLUDES, cellLinkPrefixes); @@ -524,6 +544,7 @@ public class UpstreamNetworkMonitorTest { // [5] Pretend mobile disconnected. cellAgent.fakeDisconnect(); + mLooper.dispatchAll(); local = mUNM.getLocalPrefixes(); assertPrefixSet(local, EXCLUDES, wifiLinkPrefixes); assertPrefixSet(local, EXCLUDES, cellLinkPrefixes); @@ -531,6 +552,7 @@ public class UpstreamNetworkMonitorTest { // [6] Pretend DUN disconnected. dunAgent.fakeDisconnect(); + mLooper.dispatchAll(); local = mUNM.getLocalPrefixes(); assertTrue(local.isEmpty()); } @@ -550,6 +572,7 @@ public class UpstreamNetworkMonitorTest { // Setup mobile network. final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, CELL_CAPABILITIES); cellAgent.fakeConnect(); + mLooper.dispatchAll(); assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI, mUNM.selectPreferredUpstreamType(preferredTypes)); @@ -575,8 +598,8 @@ public class UpstreamNetworkMonitorTest { } private boolean isDunRequested() { - for (NetworkRequest req : mCM.requested.values()) { - if (req.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)) { + for (NetworkRequestInfo nri : mCM.requested.values()) { + if (nri.request.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)) { return true; } } @@ -602,8 +625,8 @@ public class UpstreamNetworkMonitorTest { } } - public TestStateMachine() { - super("UpstreamNetworkMonitor.TestStateMachine"); + public TestStateMachine(Looper looper) { + super("UpstreamNetworkMonitor.TestStateMachine", looper); addState(mLoggingState); setInitialState(mLoggingState); super.start();