From f20360649fb951825ce18406c9ea7f55870f2d5b Mon Sep 17 00:00:00 2001 From: Junyu Lai Date: Mon, 24 May 2021 17:36:38 +0000 Subject: [PATCH] Add CTS for registerNetworkOffer Test: android.net.NetworkProviderTest on R/S device Bug: 189074532 Change-Id: Ic3ab3c487d8a9b5a9508b59f83ae35672307b933 --- .../java/android/net/NetworkProviderTest.kt | 184 +++++++++++++++++- 1 file changed, 180 insertions(+), 4 deletions(-) diff --git a/tests/common/java/android/net/NetworkProviderTest.kt b/tests/common/java/android/net/NetworkProviderTest.kt index 97d3c5a802..8cea12e0ea 100644 --- a/tests/common/java/android/net/NetworkProviderTest.kt +++ b/tests/common/java/android/net/NetworkProviderTest.kt @@ -18,12 +18,14 @@ package android.net import android.app.Instrumentation import android.content.Context +import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED import android.net.NetworkCapabilities.TRANSPORT_TEST import android.net.NetworkProviderTest.TestNetworkCallback.CallbackEntry.OnUnavailable import android.net.NetworkProviderTest.TestNetworkProvider.CallbackEntry.OnNetworkRequestWithdrawn import android.net.NetworkProviderTest.TestNetworkProvider.CallbackEntry.OnNetworkRequested import android.os.Build +import android.os.Handler import android.os.HandlerThread import android.os.Looper import android.util.Log @@ -34,6 +36,7 @@ import com.android.testutils.DevSdkIgnoreRule import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo import com.android.testutils.DevSdkIgnoreRunner +import com.android.testutils.TestableNetworkOfferCallback import com.android.testutils.isDevSdkInRange import org.junit.After import org.junit.Before @@ -44,11 +47,14 @@ import org.mockito.Mockito.doReturn import org.mockito.Mockito.mock import org.mockito.Mockito.verifyNoMoreInteractions import java.util.UUID +import java.util.concurrent.Executor +import java.util.concurrent.RejectedExecutionException import kotlin.test.assertEquals import kotlin.test.assertNotEquals import kotlin.test.fail private const val DEFAULT_TIMEOUT_MS = 5000L +private const val DEFAULT_NO_CALLBACK_TIMEOUT_MS = 200L private val instrumentation: Instrumentation get() = InstrumentationRegistry.getInstrumentation() private val context: Context get() = InstrumentationRegistry.getContext() @@ -102,12 +108,24 @@ class NetworkProviderTest { crossinline predicate: (T) -> Boolean ) = seenEvents.poll(DEFAULT_TIMEOUT_MS) { it is T && predicate(it) } ?: fail("Did not receive callback after ${DEFAULT_TIMEOUT_MS}ms") + + fun assertNoCallback() { + val cb = seenEvents.poll(DEFAULT_NO_CALLBACK_TIMEOUT_MS) + if (null != cb) fail("Expected no callback but got $cb") + } } private fun createNetworkProvider(ctx: Context = context): TestNetworkProvider { return TestNetworkProvider(ctx, mHandlerThread.looper) } + private fun createAndRegisterNetworkProvider(ctx: Context = context) = + createNetworkProvider(ctx).also { + assertEquals(it.getProviderId(), NetworkProvider.ID_NONE) + mCm.registerNetworkProvider(it) + assertNotEquals(it.getProviderId(), NetworkProvider.ID_NONE) + } + // In S+ framework, do not run this test, since the provider will no longer receive // onNetworkRequested for every request. Instead, provider needs to // call {@code registerNetworkOffer} with the description of networks they @@ -115,10 +133,7 @@ class NetworkProviderTest { @IgnoreAfter(Build.VERSION_CODES.R) @Test fun testOnNetworkRequested() { - val provider = createNetworkProvider() - assertEquals(provider.getProviderId(), NetworkProvider.ID_NONE) - mCm.registerNetworkProvider(provider) - assertNotEquals(provider.getProviderId(), NetworkProvider.ID_NONE) + val provider = createAndRegisterNetworkProvider() val specifier = CompatUtil.makeTestNetworkSpecifier( UUID.randomUUID().toString()) @@ -179,6 +194,167 @@ class NetworkProviderTest { mCm.unregisterNetworkProvider(provider) } + // Mainline module can't use internal HandlerExecutor, so add an identical executor here. + // TODO: Refactor with the one in MultiNetworkPolicyTracker. + private class HandlerExecutor(private val handler: Handler) : Executor { + public override fun execute(command: Runnable) { + if (!handler.post(command)) { + throw RejectedExecutionException(handler.toString() + " is shutting down") + } + } + } + + @IgnoreUpTo(Build.VERSION_CODES.R) + @Test + fun testRegisterNetworkOffer() { + val provider = createAndRegisterNetworkProvider() + val provider2 = createAndRegisterNetworkProvider() + + // Prepare the materials which will be used to create different offers. + val specifier1 = CompatUtil.makeTestNetworkSpecifier("TEST-SPECIFIER-1") + val specifier2 = CompatUtil.makeTestNetworkSpecifier("TEST-SPECIFIER-2") + val scoreWeaker = NetworkScore.Builder().build() + val scoreStronger = NetworkScore.Builder().setTransportPrimary(true).build() + val ncFilter1 = NetworkCapabilities.Builder().addTransportType(TRANSPORT_TEST) + .setNetworkSpecifier(specifier1).build() + val ncFilter2 = NetworkCapabilities.Builder().addTransportType(TRANSPORT_TEST) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + .setNetworkSpecifier(specifier1).build() + val ncFilter3 = NetworkCapabilities.Builder().addTransportType(TRANSPORT_TEST) + .setNetworkSpecifier(specifier2).build() + val ncFilter4 = NetworkCapabilities.Builder().addTransportType(TRANSPORT_TEST) + .setNetworkSpecifier(specifier2).build() + + // Make 4 offers, where 1 doesn't have NOT_VCN, 2 has NOT_VCN, 3 is similar to 1 but with + // different specifier, and 4 is also similar to 1 but with different provider. + val offerCallback1 = TestableNetworkOfferCallback( + DEFAULT_TIMEOUT_MS, DEFAULT_NO_CALLBACK_TIMEOUT_MS) + val offerCallback2 = TestableNetworkOfferCallback( + DEFAULT_TIMEOUT_MS, DEFAULT_NO_CALLBACK_TIMEOUT_MS) + val offerCallback3 = TestableNetworkOfferCallback( + DEFAULT_TIMEOUT_MS, DEFAULT_NO_CALLBACK_TIMEOUT_MS) + val offerCallback4 = TestableNetworkOfferCallback( + DEFAULT_TIMEOUT_MS, DEFAULT_NO_CALLBACK_TIMEOUT_MS) + provider.registerNetworkOffer(scoreWeaker, ncFilter1, + HandlerExecutor(mHandlerThread.threadHandler), offerCallback1) + provider.registerNetworkOffer(scoreStronger, ncFilter2, + HandlerExecutor(mHandlerThread.threadHandler), offerCallback2) + provider.registerNetworkOffer(scoreWeaker, ncFilter3, + HandlerExecutor(mHandlerThread.threadHandler), offerCallback3) + provider2.registerNetworkOffer(scoreWeaker, ncFilter4, + HandlerExecutor(mHandlerThread.threadHandler), offerCallback4) + // Unlike Android R, Android S+ provider will only receive interested requests via offer + // callback. Verify that the callbacks do not see any existing request such as default + // requests. + offerCallback1.assertNoCallback() + offerCallback2.assertNoCallback() + offerCallback3.assertNoCallback() + offerCallback4.assertNoCallback() + + // File a request with specifier but without NOT_VCN, verify network is needed for callback + // with the same specifier. + val nrNoNotVcn: NetworkRequest = NetworkRequest.Builder() + .addTransportType(TRANSPORT_TEST) + // Test network is not allowed to be trusted. + .removeCapability(NET_CAPABILITY_TRUSTED) + .setNetworkSpecifier(specifier1) + .build() + val cb1 = ConnectivityManager.NetworkCallback() + mCm.requestNetwork(nrNoNotVcn, cb1) + offerCallback1.expectOnNetworkNeeded(ncFilter1) + offerCallback2.expectOnNetworkNeeded(ncFilter2) + offerCallback3.assertNoCallback() + offerCallback4.assertNoCallback() + + mCm.unregisterNetworkCallback(cb1) + offerCallback1.expectOnNetworkUnneeded(ncFilter1) + offerCallback2.expectOnNetworkUnneeded(ncFilter2) + offerCallback3.assertNoCallback() + offerCallback4.assertNoCallback() + + // File a request without specifier but with NOT_VCN, verify network is needed for offer + // with NOT_VCN. + val nrNotVcn: NetworkRequest = NetworkRequest.Builder() + .addTransportType(TRANSPORT_TEST) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + // Test network is not allowed to be trusted. + .removeCapability(NET_CAPABILITY_TRUSTED) + .build() + val cb2 = ConnectivityManager.NetworkCallback() + mCm.requestNetwork(nrNotVcn, cb2) + offerCallback1.assertNoCallback() + offerCallback2.expectOnNetworkNeeded(ncFilter2) + offerCallback3.assertNoCallback() + offerCallback4.assertNoCallback() + + // Upgrade offer 3 & 4 to satisfy previous request and then verify they are also needed. + ncFilter3.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + provider.registerNetworkOffer(scoreWeaker, ncFilter3, + HandlerExecutor(mHandlerThread.threadHandler), offerCallback3) + ncFilter4.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + provider2.registerNetworkOffer(scoreWeaker, ncFilter4, + HandlerExecutor(mHandlerThread.threadHandler), offerCallback4) + offerCallback1.assertNoCallback() + offerCallback2.assertNoCallback() + offerCallback3.expectOnNetworkNeeded(ncFilter3) + offerCallback4.expectOnNetworkNeeded(ncFilter4) + + // Connect an agent to fulfill the request, verify offer 4 is not needed since it is not + // from currently serving provider nor can beat the current satisfier. + val nc = NetworkCapabilities().apply { + addTransportType(NetworkCapabilities.TRANSPORT_TEST) + removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) + addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) + addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED) + addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING) + addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) + setNetworkSpecifier(specifier1) + } + val config = NetworkAgentConfig.Builder().build() + val agent = object : NetworkAgent(context, mHandlerThread.looper, "TestAgent", nc, + LinkProperties(), scoreWeaker, config, provider) {} + agent.register() + agent.markConnected() + // TODO: The request is satisying by offer 2 instead of offer 1, thus it should not be + // considered as needed. + offerCallback1.expectOnNetworkNeeded(ncFilter2) + offerCallback2.assertNoCallback() // Still needed. + offerCallback3.assertNoCallback() // Still needed. + offerCallback4.expectOnNetworkUnneeded(ncFilter4) + + // Upgrade the agent, verify no change since the framework will treat the offer as needed + // if a request is currently satisfied by the network provided by the same provider. + // TODO: Consider offers with weaker score are unneeded. + agent.sendNetworkScore(scoreStronger) + offerCallback1.assertNoCallback() + offerCallback2.assertNoCallback() // Still needed. + offerCallback3.assertNoCallback() // Still needed. + offerCallback4.assertNoCallback() // Still unneeded. + + // Verify that offer callbacks cannot receive any event if offer is unregistered. + provider2.unregisterNetworkOffer(offerCallback4) + agent.unregister() + offerCallback1.assertNoCallback() // Still needed. + offerCallback2.assertNoCallback() // Still needed. + offerCallback3.assertNoCallback() // Still needed. + // Since the agent is unregistered, and the offer has chance to satisfy the request, + // this callback should receive needed if it is not unregistered. + offerCallback4.assertNoCallback() + + // Verify that offer callbacks cannot receive any event if provider is unregistered. + mCm.unregisterNetworkProvider(provider) + mCm.unregisterNetworkCallback(cb2) + offerCallback1.assertNoCallback() // Should be unneeded if not unregistered. + offerCallback2.assertNoCallback() // Should be unneeded if not unregistered. + offerCallback3.assertNoCallback() // Should be unneeded if not unregistered. + offerCallback4.assertNoCallback() // Already unregistered. + + // Clean up and Verify providers did not receive any callback during the entire test. + mCm.unregisterNetworkProvider(provider2) + provider.assertNoCallback() + provider2.assertNoCallback() + } + private class TestNetworkCallback : ConnectivityManager.NetworkCallback() { private val seenEvents = ArrayTrackRecord().newReadHead() sealed class CallbackEntry {