diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java index fe313b9feb..acec9a416d 100644 --- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java +++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java @@ -650,6 +650,18 @@ public class ConnectivityManagerTest { mCm.getBackgroundDataSetting(); } + private NetworkRequest makeDefaultRequest() { + // Make a request that is similar to the way framework tracks the system + // default network. + return new NetworkRequest.Builder() + .clearCapabilities() + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) + .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .build(); + } + private NetworkRequest makeWifiNetworkRequest() { return new NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) @@ -688,12 +700,14 @@ public class ConnectivityManagerTest { final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback(); final TestNetworkCallback perUidCallback = new TestNetworkCallback(); + final TestNetworkCallback bestMatchingCallback = new TestNetworkCallback(); final Handler h = new Handler(Looper.getMainLooper()); if (TestUtils.shouldTestSApis()) { runWithShellPermissionIdentity(() -> { mCmShim.registerSystemDefaultNetworkCallback(systemDefaultCallback, h); mCmShim.registerDefaultNetworkCallbackForUid(Process.myUid(), perUidCallback, h); }, NETWORK_SETTINGS); + mCm.registerBestMatchingNetworkCallback(makeDefaultRequest(), bestMatchingCallback, h); } Network wifiNetwork = null; @@ -719,6 +733,10 @@ public class ConnectivityManagerTest { assertNotNull("Did not receive onAvailable on per-UID default network callback", perUidNetwork); assertEquals(defaultNetwork, perUidNetwork); + final Network bestMatchingNetwork = bestMatchingCallback.waitForAvailable(); + assertNotNull("Did not receive onAvailable on best matching network callback", + bestMatchingNetwork); + assertEquals(defaultNetwork, bestMatchingNetwork); } } catch (InterruptedException e) { @@ -729,6 +747,7 @@ public class ConnectivityManagerTest { if (TestUtils.shouldTestSApis()) { mCm.unregisterNetworkCallback(systemDefaultCallback); mCm.unregisterNetworkCallback(perUidCallback); + mCm.unregisterNetworkCallback(bestMatchingCallback); } } } diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt index b67acca61d..c505cefef9 100644 --- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt +++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt @@ -66,6 +66,7 @@ import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnSta import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnStopSocketKeepalive import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnValidationStatus import android.os.Build +import android.os.Handler import android.os.HandlerThread import android.os.Looper import android.os.Message @@ -331,6 +332,15 @@ class NetworkAgentTest { callbacksToCleanUp.add(callback) } + private fun registerBestMatchingNetworkCallback( + request: NetworkRequest, + callback: TestableNetworkCallback, + handler: Handler + ) { + mCM!!.registerBestMatchingNetworkCallback(request, callback, handler) + callbacksToCleanUp.add(callback) + } + private fun makeTestNetworkRequest(specifier: String? = null): NetworkRequest { return NetworkRequest.Builder() .clearCapabilities() @@ -868,4 +878,60 @@ class NetworkAgentTest { val testNetworkSnapshot = snapshots.findLast { it.network == agent.network } assertEquals(imsi, testNetworkSnapshot!!.subscriberId) } + + @Test + @IgnoreUpTo(Build.VERSION_CODES.R) + // TODO: Refactor helper functions to util class and move this test case to + // {@link android.net.cts.ConnectivityManagerTest}. + fun testRegisterBestMatchingNetworkCallback() { + // Register best matching network callback with additional condition that will be + // exercised later. This assumes the test network agent has NOT_VCN_MANAGED in it and + // does not have NET_CAPABILITY_TEMPORARILY_NOT_METERED. + val bestMatchingCb = TestableNetworkCallback(timeoutMs = DEFAULT_TIMEOUT_MS) + registerBestMatchingNetworkCallback(NetworkRequest.Builder() + .clearCapabilities() + .addTransportType(TRANSPORT_TEST) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + .build(), bestMatchingCb, mHandlerThread.threadHandler) + + val (agent1, _) = createConnectedNetworkAgent(specifier = "AGENT-1") + bestMatchingCb.expectAvailableThenValidatedCallbacks(agent1.network!!) + // Make agent1 worse so when agent2 shows up, the callback will see that. + agent1.sendNetworkScore(NetworkScore.Builder().setExiting(true).build()) + bestMatchingCb.assertNoCallback(NO_CALLBACK_TIMEOUT) + + val (agent2, _) = createConnectedNetworkAgent(specifier = "AGENT-2") + bestMatchingCb.expectAvailableDoubleValidatedCallbacks(agent2.network!!) + + // Change something on agent1 to trigger capabilities changed, since the callback + // only cares about the best network, verify it received nothing from agent1. + val ncAgent1 = agent1.nc + ncAgent1.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED) + agent1.sendNetworkCapabilities(ncAgent1) + bestMatchingCb.assertNoCallback(NO_CALLBACK_TIMEOUT) + + // Make agent1 the best network again, verify the callback now tracks agent1. + agent1.sendNetworkScore(NetworkScore.Builder() + .setExiting(false).setTransportPrimary(true).build()) + bestMatchingCb.expectAvailableCallbacks(agent1.network!!) + + // Make agent1 temporary vcn managed, which will not satisfying the request. + // Verify the callback switch from/to the other network accordingly. + ncAgent1.removeCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + agent1.sendNetworkCapabilities(ncAgent1) + bestMatchingCb.expectAvailableCallbacks(agent2.network!!) + ncAgent1.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + agent1.sendNetworkCapabilities(ncAgent1) + bestMatchingCb.expectAvailableDoubleValidatedCallbacks(agent1.network!!) + + // Verify the callback doesn't care about agent2 disconnect. + agent2.unregister() + agentsToCleanUp.remove(agent2) + bestMatchingCb.assertNoCallback() + agent1.unregister() + agentsToCleanUp.remove(agent1) + bestMatchingCb.expectCallback(agent1.network!!) + + // tearDown() will unregister the requests and agents + } }