Merge changes I70384830,Ie97c2611,I7064edda,I290136b4,Ide697f65, ... am: bbae90476d

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/2120118

Change-Id: I5d3e36194ed3cc57fc75287d991069057f9f8a27
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
index db24b44..d0d44dc 100644
--- a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
@@ -30,11 +30,14 @@
 import android.net.EthernetManager.STATE_LINK_UP
 import android.net.EthernetManager.TetheredInterfaceCallback
 import android.net.EthernetManager.TetheredInterfaceRequest
+import android.net.EthernetNetworkManagementException
 import android.net.EthernetNetworkSpecifier
+import android.net.EthernetNetworkUpdateRequest
 import android.net.InetAddresses
 import android.net.IpConfiguration
 import android.net.MacAddress
 import android.net.Network
+import android.net.NetworkCapabilities
 import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
 import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
 import android.net.NetworkCapabilities.TRANSPORT_TEST
@@ -44,8 +47,8 @@
 import android.net.cts.EthernetManagerTest.EthernetStateListener.CallbackEntry.InterfaceStateChanged
 import android.os.Build
 import android.os.Handler
-import android.os.HandlerExecutor
 import android.os.Looper
+import android.os.OutcomeReceiver
 import android.os.SystemProperties
 import android.platform.test.annotations.AppModeFull
 import android.util.ArraySet
@@ -100,29 +103,34 @@
     private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
     private val em by lazy { context.getSystemService(EthernetManager::class.java) }
     private val cm by lazy { context.getSystemService(ConnectivityManager::class.java) }
+    private val handler by lazy { Handler(Looper.getMainLooper()) }
 
     private val ifaceListener = EthernetStateListener()
     private val createdIfaces = ArrayList<EthernetTestInterface>()
     private val addedListeners = ArrayList<EthernetStateListener>()
-    private val networkRequests = ArrayList<TestableNetworkCallback>()
+    private val registeredCallbacks = ArrayList<TestableNetworkCallback>()
 
     private var tetheredInterfaceRequest: TetheredInterfaceRequest? = null
 
     private class EthernetTestInterface(
         context: Context,
-        private val handler: Handler
+        private val handler: Handler,
+        hasCarrier: Boolean
     ) {
         private val tapInterface: TestNetworkInterface
         private val packetReader: TapPacketReader
         private val raResponder: RouterAdvertisementResponder
-        val interfaceName get() = tapInterface.interfaceName
+        private val tnm: TestNetworkManager
+        val name get() = tapInterface.interfaceName
 
         init {
-            tapInterface = runAsShell(MANAGE_TEST_NETWORKS) {
-                val tnm = context.getSystemService(TestNetworkManager::class.java)
-                tnm.createTapInterface(false /* bringUp */)
+            tnm = runAsShell(MANAGE_TEST_NETWORKS) {
+                context.getSystemService(TestNetworkManager::class.java)
             }
-            val mtu = 1500
+            tapInterface = runAsShell(MANAGE_TEST_NETWORKS) {
+                tnm.createTapInterface(hasCarrier, false /* bringUp */)
+            }
+            val mtu = tapInterface.mtu
             packetReader = TapPacketReader(handler, tapInterface.fileDescriptor.fileDescriptor, mtu)
             raResponder = RouterAdvertisementResponder(packetReader)
             raResponder.addRouterEntry(MacAddress.fromString("01:23:45:67:89:ab"),
@@ -132,6 +140,12 @@
             raResponder.start()
         }
 
+        fun setCarrierEnabled(enabled: Boolean) {
+            runAsShell(MANAGE_TEST_NETWORKS) {
+                tnm.setCarrierEnabled(tapInterface, enabled)
+            }
+        }
+
         fun destroy() {
             raResponder.stop()
             handler.post({ packetReader.stop() })
@@ -172,7 +186,7 @@
         }
 
         fun expectCallback(iface: EthernetTestInterface, state: Int, role: Int) {
-            expectCallback(createChangeEvent(iface.interfaceName, state, role))
+            expectCallback(createChangeEvent(iface.name, state, role))
         }
 
         fun createChangeEvent(iface: String, state: Int, role: Int) =
@@ -190,7 +204,7 @@
         }
 
         fun eventuallyExpect(iface: EthernetTestInterface, state: Int, role: Int) {
-            eventuallyExpect(iface.interfaceName, state, role)
+            eventuallyExpect(iface.name, state, role)
         }
 
         fun assertNoCallback() {
@@ -227,6 +241,35 @@
         }
     }
 
+    private class EthernetOutcomeReceiver :
+        OutcomeReceiver<String, EthernetNetworkManagementException> {
+        private val result = CompletableFuture<String>()
+
+        override fun onResult(iface: String) {
+            result.complete(iface)
+        }
+
+        override fun onError(e: EthernetNetworkManagementException) {
+            result.completeExceptionally(e)
+        }
+
+        fun expectResult(expected: String) {
+            assertEquals(expected, result.get(TIMEOUT_MS, TimeUnit.MILLISECONDS))
+        }
+
+        fun expectError() {
+            // Assert that the future fails with EthernetNetworkManagementException from the
+            // completeExceptionally() call inside onUnavailable.
+            assertFailsWith(EthernetNetworkManagementException::class) {
+                try {
+                    result.get()
+                } catch (e: ExecutionException) {
+                    throw e.cause!!
+                }
+            }
+        }
+    }
+
     @Before
     fun setUp() {
         setIncludeTestInterfaces(true)
@@ -243,26 +286,28 @@
         for (listener in addedListeners) {
             em.removeInterfaceStateListener(listener)
         }
-        networkRequests.forEach { cm.unregisterNetworkCallback(it) }
+        registeredCallbacks.forEach { cm.unregisterNetworkCallback(it) }
         releaseTetheredInterface()
     }
 
     private fun addInterfaceStateListener(listener: EthernetStateListener) {
         runAsShell(CONNECTIVITY_USE_RESTRICTED_NETWORKS) {
-            em.addInterfaceStateListener(HandlerExecutor(Handler(Looper.getMainLooper())), listener)
+            em.addInterfaceStateListener(handler::post, listener)
         }
         addedListeners.add(listener)
     }
 
-    private fun createInterface(): EthernetTestInterface {
+    private fun createInterface(hasCarrier: Boolean = true): EthernetTestInterface {
         val iface = EthernetTestInterface(
             context,
-            Handler(Looper.getMainLooper())
+            handler,
+            hasCarrier
         ).also { createdIfaces.add(it) }
-        with(ifaceListener) {
-            // when an interface comes up, we should always see a down cb before an up cb.
-            eventuallyExpect(iface, STATE_LINK_DOWN, ROLE_CLIENT)
-            expectCallback(iface, STATE_LINK_UP, ROLE_CLIENT)
+
+        // when an interface comes up, we should always see a down cb before an up cb.
+        ifaceListener.eventuallyExpect(iface, STATE_LINK_DOWN, ROLE_CLIENT)
+        if (hasCarrier) {
+            ifaceListener.expectCallback(iface, STATE_LINK_UP, ROLE_CLIENT)
         }
         return iface
     }
@@ -282,18 +327,20 @@
     private fun requestNetwork(request: NetworkRequest): TestableNetworkCallback {
         return TestableNetworkCallback().also {
             cm.requestNetwork(request, it)
-            networkRequests.add(it)
+            registeredCallbacks.add(it)
         }
     }
 
-    private fun releaseNetwork(cb: TestableNetworkCallback) {
-        cm.unregisterNetworkCallback(cb)
-        networkRequests.remove(cb)
+    private fun registerNetworkListener(request: NetworkRequest): TestableNetworkCallback {
+        return TestableNetworkCallback().also {
+            cm.registerNetworkCallback(request, it)
+            registeredCallbacks.add(it)
+        }
     }
 
     private fun requestTetheredInterface() = TetheredInterfaceListener().also {
         tetheredInterfaceRequest = runAsShell(NETWORK_SETTINGS) {
-            em.requestTetheredInterface(HandlerExecutor(Handler(Looper.getMainLooper())), it)
+            em.requestTetheredInterface(handler::post, it)
         }
     }
 
@@ -304,22 +351,65 @@
         }
     }
 
+    private fun releaseRequest(cb: TestableNetworkCallback) {
+        cm.unregisterNetworkCallback(cb)
+        registeredCallbacks.remove(cb)
+    }
+
+    private fun disableInterface(iface: EthernetTestInterface) = EthernetOutcomeReceiver().also {
+        runAsShell(MANAGE_TEST_NETWORKS) {
+            em.disableInterface(iface.name, handler::post, it)
+        }
+    }
+
+    private fun enableInterface(iface: EthernetTestInterface) = EthernetOutcomeReceiver().also {
+        runAsShell(MANAGE_TEST_NETWORKS) {
+            em.enableInterface(iface.name, handler::post, it)
+        }
+    }
+
+    private fun updateConfiguration(
+        iface: EthernetTestInterface,
+        ipConfig: IpConfiguration? = null,
+        capabilities: NetworkCapabilities? = null
+    ) = EthernetOutcomeReceiver().also {
+        runAsShell(MANAGE_TEST_NETWORKS) {
+            em.updateConfiguration(
+                iface.name,
+                EthernetNetworkUpdateRequest.Builder()
+                    .setIpConfiguration(ipConfig)
+                    .setNetworkCapabilities(capabilities).build(),
+                handler::post,
+                it)
+        }
+    }
+
+    // NetworkRequest.Builder does not create a copy of the passed NetworkRequest, so in order to
+    // keep ETH_REQUEST as it is, a defensive copy is created here.
     private fun NetworkRequest.createCopyWithEthernetSpecifier(ifaceName: String) =
         NetworkRequest.Builder(NetworkRequest(ETH_REQUEST))
             .setNetworkSpecifier(EthernetNetworkSpecifier(ifaceName)).build()
 
     // It can take multiple seconds for the network to become available.
     private fun TestableNetworkCallback.expectAvailable() =
-        expectCallback<Available>(anyNetwork(), 5000/*ms timeout*/).network
+        expectCallback<Available>(anyNetwork(), 5000 /* ms timeout */).network
 
     // b/233534110: eventuallyExpect<Lost>() does not advance ReadHead, use
     // eventuallyExpect(Lost::class) instead.
     private fun TestableNetworkCallback.eventuallyExpectLost(n: Network? = null) =
         eventuallyExpect(Lost::class, TIMEOUT_MS) { n?.equals(it.network) ?: true }
 
-    private fun TestableNetworkCallback.assertNotLost(n: Network? = null) =
+    private fun TestableNetworkCallback.assertNeverLost(n: Network? = null) =
         assertNoCallbackThat() { it is Lost && (n?.equals(it.network) ?: true) }
 
+    private fun TestableNetworkCallback.assertNeverAvailable(n: Network? = null) =
+        assertNoCallbackThat() { it is Available && (n?.equals(it.network) ?: true) }
+
+    private fun TestableNetworkCallback.expectCapabilitiesWithInterfaceName(name: String) =
+        expectCapabilitiesThat(anyNetwork()) {
+            it.networkSpecifier == EthernetNetworkSpecifier(name)
+        }
+
     @Test
     fun testCallbacks() {
         // If an interface exists when the callback is registered, it is reported on registration.
@@ -398,11 +488,8 @@
             assertTrue(polledIfaces.add(iface), "Duplicate interface $iface returned")
             assertTrue(ifaces.contains(iface), "Untracked interface $iface returned")
             // If the event's iface was created in the test, additional criteria can be validated.
-            createdIfaces.find { it.interfaceName.equals(iface) }?.let {
-                assertEquals(event,
-                    listener.createChangeEvent(it.interfaceName,
-                                                        STATE_LINK_UP,
-                                                        ROLE_CLIENT))
+            createdIfaces.find { it.name.equals(iface) }?.let {
+                assertEquals(event, listener.createChangeEvent(it.name, STATE_LINK_UP, ROLE_CLIENT))
             }
         }
         // Assert all callbacks are accounted for.
@@ -411,87 +498,75 @@
 
     @Test
     fun testGetInterfaceList() {
-        setIncludeTestInterfaces(true)
-
         // Create two test interfaces and check the return list contains the interface names.
         val iface1 = createInterface()
         val iface2 = createInterface()
         var ifaces = em.getInterfaceList()
         assertTrue(ifaces.size > 0)
-        assertTrue(ifaces.contains(iface1.interfaceName))
-        assertTrue(ifaces.contains(iface2.interfaceName))
+        assertTrue(ifaces.contains(iface1.name))
+        assertTrue(ifaces.contains(iface2.name))
 
         // Remove one existing test interface and check the return list doesn't contain the
         // removed interface name.
         removeInterface(iface1)
         ifaces = em.getInterfaceList()
-        assertFalse(ifaces.contains(iface1.interfaceName))
-        assertTrue(ifaces.contains(iface2.interfaceName))
+        assertFalse(ifaces.contains(iface1.name))
+        assertTrue(ifaces.contains(iface2.name))
 
         removeInterface(iface2)
     }
 
     @Test
     fun testNetworkRequest_withSingleExistingInterface() {
-        setIncludeTestInterfaces(true)
         createInterface()
 
         // install a listener which will later be used to verify the Lost callback
-        val listenerCb = TestableNetworkCallback()
-        cm.registerNetworkCallback(ETH_REQUEST, listenerCb)
-        networkRequests.add(listenerCb)
+        val listenerCb = registerNetworkListener(ETH_REQUEST)
 
         val cb = requestNetwork(ETH_REQUEST)
         val network = cb.expectAvailable()
 
-        cb.assertNotLost()
-        releaseNetwork(cb)
+        cb.assertNeverLost()
+        releaseRequest(cb)
         listenerCb.eventuallyExpectLost(network)
     }
 
     @Test
     fun testNetworkRequest_beforeSingleInterfaceIsUp() {
-        setIncludeTestInterfaces(true)
-
         val cb = requestNetwork(ETH_REQUEST)
 
-        // bring up interface after network has been requested
+        // bring up interface after network has been requested.
+        // Note: there is no guarantee that the NetworkRequest has been processed before the
+        // interface is actually created. That being said, it takes a few seconds between calling
+        // createInterface and the interface actually being properly registered with the ethernet
+        // module, so it is extremely unlikely that the CS handler thread has not run until then.
         val iface = createInterface()
         val network = cb.expectAvailable()
 
         // remove interface before network request has been removed
-        cb.assertNotLost()
+        cb.assertNeverLost()
         removeInterface(iface)
         cb.eventuallyExpectLost()
-
-        releaseNetwork(cb)
     }
 
     @Test
     fun testNetworkRequest_withMultipleInterfaces() {
-        setIncludeTestInterfaces(true)
-
         val iface1 = createInterface()
         val iface2 = createInterface()
 
-        val cb = requestNetwork(ETH_REQUEST.createCopyWithEthernetSpecifier(iface2.interfaceName))
+        val cb = requestNetwork(ETH_REQUEST.createCopyWithEthernetSpecifier(iface2.name))
 
         val network = cb.expectAvailable()
-        cb.expectCapabilitiesThat(network) {
-            it.networkSpecifier == EthernetNetworkSpecifier(iface2.interfaceName)
-        }
+        cb.expectCapabilitiesWithInterfaceName(iface2.name)
 
         removeInterface(iface1)
-        cb.assertNotLost()
+        cb.assertNeverLost()
         removeInterface(iface2)
         cb.eventuallyExpectLost()
-
-        releaseNetwork(cb)
     }
 
     @Test
     fun testNetworkRequest_withInterfaceBeingReplaced() {
-        setIncludeTestInterfaces(true)
         val iface1 = createInterface()
 
         val cb = requestNetwork(ETH_REQUEST)
@@ -499,36 +574,67 @@
 
         // create another network and verify the request sticks to the current network
         val iface2 = createInterface()
-        cb.assertNotLost()
+        cb.assertNeverLost()
 
         // remove iface1 and verify the request brings up iface2
         removeInterface(iface1)
         cb.eventuallyExpectLost(network)
         val network2 = cb.expectAvailable()
-
-        releaseNetwork(cb)
     }
 
     @Test
     fun testNetworkRequest_withMultipleInterfacesAndRequests() {
-        setIncludeTestInterfaces(true)
         val iface1 = createInterface()
         val iface2 = createInterface()
 
-        val cb1 = requestNetwork(ETH_REQUEST.createCopyWithEthernetSpecifier(iface1.interfaceName))
-        val cb2 = requestNetwork(ETH_REQUEST.createCopyWithEthernetSpecifier(iface2.interfaceName))
+        val cb1 = requestNetwork(ETH_REQUEST.createCopyWithEthernetSpecifier(iface1.name))
+        val cb2 = requestNetwork(ETH_REQUEST.createCopyWithEthernetSpecifier(iface2.name))
         val cb3 = requestNetwork(ETH_REQUEST)
 
         cb1.expectAvailable()
+        cb1.expectCapabilitiesWithInterfaceName(iface1.name)
         cb2.expectAvailable()
+        cb2.expectCapabilitiesWithInterfaceName(iface2.name)
+        // this request can be matched by either network.
         cb3.expectAvailable()
 
-        cb1.assertNotLost()
-        cb2.assertNotLost()
-        cb3.assertNotLost()
+        cb1.assertNeverLost()
+        cb2.assertNeverLost()
+        cb3.assertNeverLost()
+    }
 
-        releaseNetwork(cb1)
-        releaseNetwork(cb2)
-        releaseNetwork(cb3)
+    @Test
+    fun testNetworkRequest_ensureProperRefcounting() {
+        // create first request before interface is up / exists; create another request after it has
+        // been created; release one of them and check that the network stays up.
+        val listener = registerNetworkListener(ETH_REQUEST)
+        val cb1 = requestNetwork(ETH_REQUEST)
+
+        val iface = createInterface()
+        val network = cb1.expectAvailable()
+
+        val cb2 = requestNetwork(ETH_REQUEST)
+        cb2.expectAvailable()
+
+        // release the first request; this used to trigger b/197548738
+        releaseRequest(cb1)
+
+        cb2.assertNeverLost()
+        releaseRequest(cb2)
+        listener.eventuallyExpectLost(network)
+    }
+
+    @Test
+    fun testNetworkRequest_forInterfaceWhileTogglingCarrier() {
+        val iface = createInterface(false /* hasCarrier */)
+
+        val cb = requestNetwork(ETH_REQUEST)
+        cb.assertNeverAvailable()
+
+        iface.setCarrierEnabled(true)
+        cb.expectAvailable()
+
+        iface.setCarrierEnabled(false)
+        cb.eventuallyExpectLost()
     }
 }