diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java index 10b3dc8925..f8b47d2a9f 100755 --- a/service/src/com/android/server/ConnectivityService.java +++ b/service/src/com/android/server/ConnectivityService.java @@ -3650,8 +3650,18 @@ public class ConnectivityService extends IConnectivityManager.Stub break; } case NetworkAgent.EVENT_UNREGISTER_AFTER_REPLACEMENT: { - // If nai is not yet created, or is already destroyed, ignore. - if (!shouldDestroyNativeNetwork(nai)) break; + if (!nai.isCreated()) { + Log.d(TAG, "unregisterAfterReplacement on uncreated " + nai.toShortString() + + ", tearing down instead"); + teardownUnneededNetwork(nai); + break; + } + + if (nai.isDestroyed()) { + Log.d(TAG, "unregisterAfterReplacement on destroyed " + nai.toShortString() + + ", ignoring"); + break; + } final int timeoutMs = (int) arg.second; if (timeoutMs < 0 || timeoutMs > NetworkAgent.MAX_TEARDOWN_DELAY_MS) { diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt index d2cd7aaad7..867d672b34 100644 --- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt +++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt @@ -335,6 +335,28 @@ class NetworkAgentTest { mFakeConnectivityService.connect(it.registerForTest(Network(FAKE_NET_ID))) } + fun assertLinkPropertiesEventually( + n: Network, + description: String, + condition: (LinkProperties?) -> Boolean + ): LinkProperties? { + val deadline = SystemClock.elapsedRealtime() + DEFAULT_TIMEOUT_MS + do { + val lp = mCM.getLinkProperties(n) + if (condition(lp)) return lp + SystemClock.sleep(10 /* ms */) + } while (SystemClock.elapsedRealtime() < deadline) + fail("Network $n LinkProperties did not $description after $DEFAULT_TIMEOUT_MS ms") + } + + fun assertLinkPropertiesEventuallyNotNull(n: Network) { + assertLinkPropertiesEventually(n, "become non-null") { it != null } + } + + fun assertLinkPropertiesEventuallyNull(n: Network) { + assertLinkPropertiesEventually(n, "become null") { it == null } + } + @Test fun testSetSubtypeNameAndExtraInfoByAgentConfig() { val subtypeLTE = TelephonyManager.NETWORK_TYPE_LTE @@ -1269,8 +1291,40 @@ class NetworkAgentTest { agent5.expectCallback() agent5.expectCallback() + // If unregisterAfterReplacement is called before markConnected, the network disconnects. + val specifier6 = UUID.randomUUID().toString() + val callback = TestableNetworkCallback() + requestNetwork(makeTestNetworkRequest(specifier = specifier6), callback) + val agent6 = createNetworkAgent(specifier = specifier6) + val network6 = agent6.register() + // No callbacks are sent, so check the LinkProperties to see if the network has connected. + assertLinkPropertiesEventuallyNotNull(agent6.network!!) + + // unregisterAfterReplacement tears down the network immediately. + // Approximately check that this is the case by picking an unregister timeout that's longer + // than the timeout of the expectCallback below. + // TODO: consider adding configurable timeouts to TestableNetworkAgent expectations. + val timeoutMs = agent6.DEFAULT_TIMEOUT_MS.toInt() + 1_000 + agent6.unregisterAfterReplacement(timeoutMs) + agent6.expectCallback() + if (!SdkLevel.isAtLeastT()) { + // Before T, onNetworkDestroyed is called even if the network was never created. + agent6.expectCallback() + } + // Poll for LinkProperties becoming null, because when onNetworkUnwanted is called, the + // network has not yet been removed from the CS data structures. + assertLinkPropertiesEventuallyNull(agent6.network!!) + assertFalse(mCM.getAllNetworks().contains(agent6.network!!)) + + // After unregisterAfterReplacement is called, the network is no longer usable and + // markConnected has no effect. + agent6.markConnected() + agent6.assertNoCallback() + assertNull(mCM.getLinkProperties(agent6.network!!)) + matchAllCallback.assertNoCallback(200 /* timeoutMs */) + // If wifi is replaced within the timeout, the device does not switch to cellular. - val (cellAgent, cellNetwork) = connectNetwork(TRANSPORT_CELLULAR) + val (_, cellNetwork) = connectNetwork(TRANSPORT_CELLULAR) testCallback.expectAvailableThenValidatedCallbacks(cellNetwork) matchAllCallback.expectAvailableThenValidatedCallbacks(cellNetwork)