From 6dcf2e24c59905d58d10ebad791e6277b60a8c01 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 27 Jun 2016 16:44:07 +0900 Subject: [PATCH 1/2] Add more test coverage for requests and callbacks. 1. Support multiple callbacks in TestNetworkCallback. This is necessary to test situations where multiple callbacks are generated by the same event (e.g., CALLBACK_LOSING on cell with CALLBACK_AVAILABLE on wifi when wifi connects), which is necessary to test callback order. So far this has not been covered because all callback testing was using per-network callbacks. 2. Add a benchmark test for registering NetworkRequests and for sending onAvailable and onLosing callbacks. Bug: 23113288 Change-Id: Ib69373358ad766ab1bd989e864a5a51ef762c73c --- .../server/ConnectivityServiceTest.java | 227 +++++++++++++----- 1 file changed, 164 insertions(+), 63 deletions(-) diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java index 68a35f1b91..d654dfc1bb 100644 --- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java @@ -79,6 +79,7 @@ import com.android.server.net.NetworkPinner; import java.net.InetAddress; import java.util.ArrayList; +import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -694,6 +695,13 @@ public class ConnectivityServiceTest extends AndroidTestCase { mCm.bindProcessToNetwork(null); } + public void tearDown() throws Exception { + if (mCellNetworkAgent != null) { mCellNetworkAgent.disconnect(); } + if (mWiFiNetworkAgent != null) { mWiFiNetworkAgent.disconnect(); } + mCellNetworkAgent = mWiFiNetworkAgent = null; + super.tearDown(); + } + private int transportToLegacyType(int transport) { switch (transport) { case TRANSPORT_WIFI: @@ -911,8 +919,6 @@ public class ConnectivityServiceTest extends AndroidTestCase { mWiFiNetworkAgent.adjustScore(11); waitFor(cv); verifyActiveNetwork(TRANSPORT_WIFI); - mCellNetworkAgent.disconnect(); - mWiFiNetworkAgent.disconnect(); } @LargeTest @@ -984,8 +990,6 @@ public class ConnectivityServiceTest extends AndroidTestCase { assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); verifyActiveNetwork(TRANSPORT_WIFI); - mCellNetworkAgent.disconnect(); - mWiFiNetworkAgent.disconnect(); } @LargeTest @@ -1012,8 +1016,6 @@ public class ConnectivityServiceTest extends AndroidTestCase { assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); verifyActiveNetwork(TRANSPORT_WIFI); - mCellNetworkAgent.disconnect(); - mWiFiNetworkAgent.disconnect(); } enum CallbackState { @@ -1029,59 +1031,77 @@ public class ConnectivityServiceTest extends AndroidTestCase { * received. assertNoCallback may be called at any time. */ private class TestNetworkCallback extends NetworkCallback { - private final ConditionVariable mConditionVariable = new ConditionVariable(); - private CallbackState mLastCallback = CallbackState.NONE; - private Network mLastNetwork; + private class CallbackInfo { + public final CallbackState state; + public final Network network; + public CallbackInfo(CallbackState s, Network n) { state = s; network = n; } + public String toString() { return String.format("%s (%s)", state, network); } + public boolean equals(Object o) { + if (!(o instanceof CallbackInfo)) return false; + CallbackInfo other = (CallbackInfo) o; + return state == other.state && Objects.equals(network, other.network); + } + } + private final LinkedBlockingQueue mCallbacks = new LinkedBlockingQueue<>(); + + private void setLastCallback(CallbackState state, Network network) { + mCallbacks.offer(new CallbackInfo(state, network)); + } public void onAvailable(Network network) { - assertEquals(CallbackState.NONE, mLastCallback); - mLastCallback = CallbackState.AVAILABLE; - mLastNetwork = network; - mConditionVariable.open(); + setLastCallback(CallbackState.AVAILABLE, network); } public void onLosing(Network network, int maxMsToLive) { - assertEquals(CallbackState.NONE, mLastCallback); - mLastCallback = CallbackState.LOSING; - mLastNetwork = network; - mConditionVariable.open(); + setLastCallback(CallbackState.LOSING, network); } public void onLost(Network network) { - assertEquals(CallbackState.NONE, mLastCallback); - mLastCallback = CallbackState.LOST; - mLastNetwork = network; - mConditionVariable.open(); - } - - void expectCallback(CallbackState state) { - expectCallback(state, null); + setLastCallback(CallbackState.LOST, network); } void expectCallback(CallbackState state, MockNetworkAgent mockAgent) { - waitFor(mConditionVariable); - assertEquals(state, mLastCallback); - if (mockAgent != null) { - assertEquals(mockAgent.getNetwork(), mLastNetwork); + CallbackInfo expected = new CallbackInfo( + state, + (mockAgent != null) ? mockAgent.getNetwork() : null); + CallbackInfo actual; + try { + actual = mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + fail("Did not receive expected " + expected + " after " + TIMEOUT_MS + "ms"); + actual = null; // Or the compiler says it might be uninitialized. } - mLastCallback = CallbackState.NONE; - mLastNetwork = null; - mConditionVariable.close(); + + assertEquals("Unexpected callback:", expected, actual); } void assertNoCallback() { - assertEquals(CallbackState.NONE, mLastCallback); + mService.waitForIdle(); + CallbackInfo c = mCallbacks.peek(); + assertNull("Unexpected callback: " + c, c); + } + } + + // Can't be part of TestNetworkCallback because "cannot be declared static; static methods can + // only be declared in a static or top level type". + static void assertNoCallbacks(TestNetworkCallback ... callbacks) { + for (TestNetworkCallback c : callbacks) { + c.assertNoCallback(); } } @LargeTest public void testStateChangeNetworkCallbacks() throws Exception { + final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback(); final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback(); final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); + final NetworkRequest genericRequest = new NetworkRequest.Builder() + .clearCapabilities().build(); final NetworkRequest wifiRequest = new NetworkRequest.Builder() .addTransportType(TRANSPORT_WIFI).build(); final NetworkRequest cellRequest = new NetworkRequest.Builder() .addTransportType(TRANSPORT_CELLULAR).build(); + mCm.registerNetworkCallback(genericRequest, genericNetworkCallback); mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback); mCm.registerNetworkCallback(cellRequest, cellNetworkCallback); @@ -1089,65 +1109,74 @@ public class ConnectivityServiceTest extends AndroidTestCase { ConditionVariable cv = waitForConnectivityBroadcasts(1); mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(false); - cellNetworkCallback.expectCallback(CallbackState.AVAILABLE); - wifiNetworkCallback.assertNoCallback(); + genericNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); waitFor(cv); + assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); // This should not trigger spurious onAvailable() callbacks, b/21762680. mCellNetworkAgent.adjustScore(-1); mService.waitForIdle(); - wifiNetworkCallback.assertNoCallback(); - cellNetworkCallback.assertNoCallback(); + assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); cv = waitForConnectivityBroadcasts(2); mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); - wifiNetworkCallback.expectCallback(CallbackState.AVAILABLE); - cellNetworkCallback.assertNoCallback(); + genericNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + wifiNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); waitFor(cv); + assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); cv = waitForConnectivityBroadcasts(2); mWiFiNetworkAgent.disconnect(); - wifiNetworkCallback.expectCallback(CallbackState.LOST); + genericNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); + wifiNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); cellNetworkCallback.assertNoCallback(); waitFor(cv); + assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); cv = waitForConnectivityBroadcasts(1); mCellNetworkAgent.disconnect(); - cellNetworkCallback.expectCallback(CallbackState.LOST); - wifiNetworkCallback.assertNoCallback(); + genericNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); + cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); waitFor(cv); + assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); // Test validated networks mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); - cellNetworkCallback.expectCallback(CallbackState.AVAILABLE); - wifiNetworkCallback.assertNoCallback(); + genericNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); // This should not trigger spurious onAvailable() callbacks, b/21762680. mCellNetworkAgent.adjustScore(-1); mService.waitForIdle(); - wifiNetworkCallback.assertNoCallback(); - cellNetworkCallback.assertNoCallback(); + assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); - wifiNetworkCallback.expectCallback(CallbackState.AVAILABLE); - cellNetworkCallback.expectCallback(CallbackState.LOSING); + genericNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + genericNetworkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent); + wifiNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + cellNetworkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); mWiFiNetworkAgent.disconnect(); - wifiNetworkCallback.expectCallback(CallbackState.LOST); - cellNetworkCallback.assertNoCallback(); + genericNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); + wifiNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); + assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); mCellNetworkAgent.disconnect(); - cellNetworkCallback.expectCallback(CallbackState.LOST); - wifiNetworkCallback.assertNoCallback(); + genericNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); + cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); + assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); } private void tryNetworkFactoryRequests(int capability) throws Exception { @@ -1314,7 +1343,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS); mCellNetworkAgent.connectWithoutInternet(); - networkCallback.expectCallback(CallbackState.AVAILABLE); + networkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); verifyActiveNetwork(TRANSPORT_WIFI); // Test releasing NetworkRequest disconnects cellular with MMS cv = mCellNetworkAgent.getDisconnectedCV(); @@ -1340,7 +1369,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { MockNetworkAgent mmsNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mmsNetworkAgent.addCapability(NET_CAPABILITY_MMS); mmsNetworkAgent.connectWithoutInternet(); - networkCallback.expectCallback(CallbackState.AVAILABLE); + networkCallback.expectCallback(CallbackState.AVAILABLE, mmsNetworkAgent); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test releasing MMS NetworkRequest does not disconnect main cellular NetworkAgent cv = mmsNetworkAgent.getDisconnectedCV(); @@ -1366,36 +1395,36 @@ public class ConnectivityServiceTest extends AndroidTestCase { mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); String firstRedirectUrl = "http://example.com/firstPath"; mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl); - captivePortalCallback.expectCallback(CallbackState.AVAILABLE); + captivePortalCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), firstRedirectUrl); // Take down network. // Expect onLost callback. mWiFiNetworkAgent.disconnect(); - captivePortalCallback.expectCallback(CallbackState.LOST); + captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); // Bring up a network with a captive portal. // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL. mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); String secondRedirectUrl = "http://example.com/secondPath"; mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl); - captivePortalCallback.expectCallback(CallbackState.AVAILABLE); + captivePortalCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), secondRedirectUrl); // Make captive portal disappear then revalidate. // Expect onLost callback because network no longer provides NET_CAPABILITY_CAPTIVE_PORTAL. mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 204; mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true); - captivePortalCallback.expectCallback(CallbackState.LOST); + captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); // Expect NET_CAPABILITY_VALIDATED onAvailable callback. - validatedCallback.expectCallback(CallbackState.AVAILABLE); + validatedCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); // Break network connectivity. // Expect NET_CAPABILITY_VALIDATED onLost callback. mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 500; mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false); - validatedCallback.expectCallback(CallbackState.LOST); + validatedCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); } @SmallTest @@ -1410,7 +1439,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { // do nothing - should get here } - assertTrue("NetworkReqeuest builder with MATCH_ALL_REQUESTS_NETWORK_SPECIFIER", + assertTrue("NetworkRequest builder with MATCH_ALL_REQUESTS_NETWORK_SPECIFIER", execptionCalled); try { @@ -1477,6 +1506,78 @@ public class ConnectivityServiceTest extends AndroidTestCase { defaultNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); } + @SmallTest + public void testRequestBenchmark() throws Exception { + // Benchmarks connecting and switching performance in the presence of a large number of + // NetworkRequests. + // 1. File NUM_REQUESTS requests. + // 2. Have a network connect. Wait for NUM_REQUESTS onAvailable callbacks to fire. + // 3. Have a new network connect and outscore the previous. Wait for NUM_REQUESTS onLosing + // and NUM_REQUESTS onAvailable callbacks to fire. + // See how long it took. + final int NUM_REQUESTS = 90; + final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build(); + final NetworkCallback[] callbacks = new NetworkCallback[NUM_REQUESTS]; + final CountDownLatch availableLatch = new CountDownLatch(NUM_REQUESTS); + final CountDownLatch losingLatch = new CountDownLatch(NUM_REQUESTS); + + final int REGISTER_TIME_LIMIT_MS = 100; + long startTime = System.currentTimeMillis(); + for (int i = 0; i < NUM_REQUESTS; i++) { + callbacks[i] = new NetworkCallback() { + @Override public void onAvailable(Network n) { availableLatch.countDown(); } + @Override public void onLosing(Network n, int t) { losingLatch.countDown(); } + }; + mCm.registerNetworkCallback(request, callbacks[i]); + } + long timeTaken = System.currentTimeMillis() - startTime; + String msg = String.format("Register %d callbacks: %dms, acceptable %dms", + NUM_REQUESTS, timeTaken, REGISTER_TIME_LIMIT_MS); + Log.d(TAG, msg); + assertTrue(msg, timeTaken < REGISTER_TIME_LIMIT_MS); + + final int CONNECT_TIME_LIMIT_MS = 30; + mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + // Don't request that the network validate, because otherwise connect() will block until + // the network gets NET_CAPABILITY_VALIDATED, after all the callbacks below have fired, + // and we won't actually measure anything. + mCellNetworkAgent.connect(false); + startTime = System.currentTimeMillis(); + if (!availableLatch.await(CONNECT_TIME_LIMIT_MS, TimeUnit.MILLISECONDS)) { + fail(String.format("Only dispatched %d/%d onAvailable callbacks in %dms", + NUM_REQUESTS - availableLatch.getCount(), NUM_REQUESTS, + CONNECT_TIME_LIMIT_MS)); + } + timeTaken = System.currentTimeMillis() - startTime; + Log.d(TAG, String.format("Connect, %d callbacks: %dms, acceptable %dms", + NUM_REQUESTS, timeTaken, CONNECT_TIME_LIMIT_MS)); + + final int SWITCH_TIME_LIMIT_MS = 30; + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + // Give wifi a high enough score that we'll linger cell when wifi comes up. + mWiFiNetworkAgent.adjustScore(40); + mWiFiNetworkAgent.connect(false); + startTime = System.currentTimeMillis(); + if (!losingLatch.await(SWITCH_TIME_LIMIT_MS, TimeUnit.MILLISECONDS)) { + fail(String.format("Only dispatched %d/%d onLosing callbacks in %dms", + NUM_REQUESTS - losingLatch.getCount(), NUM_REQUESTS, SWITCH_TIME_LIMIT_MS)); + } + timeTaken = System.currentTimeMillis() - startTime; + Log.d(TAG, String.format("Linger, %d callbacks: %dms, acceptable %dms", + NUM_REQUESTS, timeTaken, SWITCH_TIME_LIMIT_MS)); + + final int UNREGISTER_TIME_LIMIT_MS = 10; + startTime = System.currentTimeMillis(); + for (int i = 0; i < NUM_REQUESTS; i++) { + mCm.unregisterNetworkCallback(callbacks[i]); + } + timeTaken = System.currentTimeMillis() - startTime; + msg = String.format("Unregister %d callbacks: %dms, acceptable %dms", + NUM_REQUESTS, timeTaken, UNREGISTER_TIME_LIMIT_MS); + Log.d(TAG, msg); + assertTrue(msg, timeTaken < UNREGISTER_TIME_LIMIT_MS); + } + @SmallTest public void testMobileDataAlwaysOn() throws Exception { final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); @@ -1520,7 +1621,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); testFactory.expectAddRequests(2); // Because the cell request changes score twice. mCellNetworkAgent.connect(true); - cellNetworkCallback.expectCallback(CallbackState.AVAILABLE); + cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); testFactory.waitForNetworkRequests(2); assertFalse(testFactory.getMyStartRequested()); // Because the cell network outscores us. @@ -1536,7 +1637,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { testFactory.waitForNetworkRequests(1); // ... and cell data to be torn down. - cellNetworkCallback.expectCallback(CallbackState.LOST); + cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); assertEquals(1, mCm.getAllNetworks().length); testFactory.unregister(); From 2dfee890866048e540d2ea0ba49ae2bcc0a0d947 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 28 Jun 2016 21:28:28 +0900 Subject: [PATCH 2/2] Add detailed test coverage for lingering. Bug: 23113288 Change-Id: I65784f3589378d97f3c824e600f21f361be8741e --- .../server/ConnectivityServiceTest.java | 165 +++++++++++++++++- 1 file changed, 159 insertions(+), 6 deletions(-) diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java index d654dfc1bb..14744d0ece 100644 --- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java @@ -17,6 +17,7 @@ package com.android.server; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.TYPE_ETHERNET; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.getNetworkTypeName; @@ -101,6 +102,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { private WrappedConnectivityManager mCm; private MockNetworkAgent mWiFiNetworkAgent; private MockNetworkAgent mCellNetworkAgent; + private MockNetworkAgent mEthernetNetworkAgent; // This class exists to test bindProcessToNetwork and getBoundNetworkForProcess. These methods // do not go through ConnectivityService but talk to netd directly, so they don't automatically @@ -244,6 +246,9 @@ public class ConnectivityServiceTest extends AndroidTestCase { mNetworkCapabilities = new NetworkCapabilities(); mNetworkCapabilities.addTransportType(transport); switch (transport) { + case TRANSPORT_ETHERNET: + mScore = 70; + break; case TRANSPORT_WIFI: mScore = 60; break; @@ -305,6 +310,11 @@ public class ConnectivityServiceTest extends AndroidTestCase { mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); } + public void removeCapability(int capability) { + mNetworkCapabilities.removeCapability(capability); + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } + public void setSignalStrength(int signalStrength) { mNetworkCapabilities.setSignalStrength(signalStrength); mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); @@ -704,12 +714,14 @@ public class ConnectivityServiceTest extends AndroidTestCase { private int transportToLegacyType(int transport) { switch (transport) { + case TRANSPORT_ETHERNET: + return TYPE_ETHERNET; case TRANSPORT_WIFI: return TYPE_WIFI; case TRANSPORT_CELLULAR: return TYPE_MOBILE; default: - throw new IllegalStateException("Unknown transport" + transport); + throw new IllegalStateException("Unknown transport " + transport); } } @@ -1064,15 +1076,12 @@ public class ConnectivityServiceTest extends AndroidTestCase { CallbackInfo expected = new CallbackInfo( state, (mockAgent != null) ? mockAgent.getNetwork() : null); - CallbackInfo actual; try { - actual = mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS); + assertEquals("Unexpected callback:", + expected, mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)); } catch (InterruptedException e) { fail("Did not receive expected " + expected + " after " + TIMEOUT_MS + "ms"); - actual = null; // Or the compiler says it might be uninitialized. } - - assertEquals("Unexpected callback:", expected, actual); } void assertNoCallback() { @@ -1179,6 +1188,150 @@ public class ConnectivityServiceTest extends AndroidTestCase { assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); } + @SmallTest + public void testMultipleLingering() { + NetworkRequest request = new NetworkRequest.Builder() + .clearCapabilities().addCapability(NET_CAPABILITY_NOT_METERED) + .build(); + TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(request, callback); + + TestNetworkCallback defaultCallback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(defaultCallback); + + mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET); + + mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + mEthernetNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + + mCellNetworkAgent.connect(true); + callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + mWiFiNetworkAgent.connect(true); + // We get AVAILABLE on wifi when wifi connects and satisfies our unmetered request. + // We then get LOSING when wifi validates and cell is outscored. + callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + mEthernetNetworkAgent.connect(true); + callback.expectCallback(CallbackState.AVAILABLE, mEthernetNetworkAgent); + callback.expectCallback(CallbackState.LOSING, mWiFiNetworkAgent); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mEthernetNetworkAgent); + assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + mEthernetNetworkAgent.disconnect(); + callback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent); + defaultCallback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + + for (int i = 0; i < 4; i++) { + MockNetworkAgent oldNetwork, newNetwork; + if (i % 2 == 0) { + mWiFiNetworkAgent.adjustScore(-15); + oldNetwork = mWiFiNetworkAgent; + newNetwork = mCellNetworkAgent; + } else { + mWiFiNetworkAgent.adjustScore(15); + oldNetwork = mCellNetworkAgent; + newNetwork = mWiFiNetworkAgent; + + } + callback.expectCallback(CallbackState.LOSING, oldNetwork); + defaultCallback.expectCallback(CallbackState.AVAILABLE, newNetwork); + assertEquals(newNetwork.getNetwork(), mCm.getActiveNetwork()); + } + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + // Verify that if a network no longer satisfies a request, we send LOST and not LOSING, even + // if the network is still up. + mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED); + callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); + + // Wifi no longer satisfies our listen, which is for an unmetered network. + // But because its score is 55, it's still up (and the default network). + defaultCallback.assertNoCallback(); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + // Disconnect our test networks. + mWiFiNetworkAgent.disconnect(); + defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + mCellNetworkAgent.disconnect(); + defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); + + mCm.unregisterNetworkCallback(callback); + mService.waitForIdle(); + + // Check that a network is only lingered or torn down if it would not satisfy a request even + // if it validated. + request = new NetworkRequest.Builder().clearCapabilities().build(); + callback = new TestNetworkCallback(); + + mCm.registerNetworkCallback(request, callback); + + mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(false); // Score: 10 + callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + // Bring up wifi with a score of 20. + // Cell stays up because it would satisfy the default request if it validated. + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); // Score: 20 + callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); + defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + // Bring up wifi with a score of 70. + // Cell is lingered because it would not satisfy any reques, even if it validated. + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent.adjustScore(50); + mWiFiNetworkAgent.connect(false); // Score: 70 + callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent); + callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + // Tear down wifi. + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); + defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + // Bring up wifi, then validate it. In this case we do not linger cell. What happens is that + // when wifi connects, we don't linger because cell could potentially become the default + // network if it validated. Then, when wifi validates, we re-evaluate cell, see it has no + // requests, and tear it down because it's unneeded. + // TODO: can we linger in this case? + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + callback.expectCallback(CallbackState.LOST, mCellNetworkAgent); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); + + mCm.unregisterNetworkCallback(callback); + mCm.unregisterNetworkCallback(defaultCallback); + } + private void tryNetworkFactoryRequests(int capability) throws Exception { // Verify NOT_RESTRICTED is set appropriately final NetworkCapabilities nc = new NetworkRequest.Builder().addCapability(capability)