From 284888ea6acbf2543520fa2336af24d00a567397 Mon Sep 17 00:00:00 2001 From: Patrick Rohr Date: Fri, 8 Apr 2022 15:48:41 +0200 Subject: [PATCH 1/3] Clean up permission usage in EthernetManagerTest Wrap all functions that require adapting shell permissions in a private helper function. Test: atest CtsNetTestCases:EthernetManagerTest Change-Id: Ic2df53f184bd0f83767d6dff3c47a53b0b7442e2 --- .../android/net/cts/EthernetManagerTest.kt | 50 ++++++++----------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt index 2737258ae3..56361d8500 100644 --- a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt +++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt @@ -121,18 +121,14 @@ class EthernetManagerTest { val executor = HandlerExecutor(Handler(Looper.getMainLooper())) // If an interface exists when the callback is registered, it is reported on registration. - val iface = runAsShell(MANAGE_TEST_NETWORKS) { - createInterface() - } + val iface = createInterface() val listener = EthernetStateListener() addInterfaceStateListener(executor, listener) listener.expectCallback(iface, STATE_LINK_UP, ROLE_CLIENT) // If an interface appears, existing callbacks see it. // TODO: fix the up/up/down/up callbacks and only send down/up. - val iface2 = runAsShell(MANAGE_TEST_NETWORKS) { - createInterface() - } + val iface2 = createInterface() listener.expectCallback(iface2, STATE_LINK_UP, ROLE_CLIENT) listener.expectCallback(iface2, STATE_LINK_UP, ROLE_CLIENT) listener.expectCallback(iface2, STATE_LINK_DOWN, ROLE_CLIENT) @@ -151,21 +147,17 @@ class EthernetManagerTest { @Before fun setUp() { - runAsShell(MANAGE_TEST_NETWORKS, NETWORK_SETTINGS) { - em.setIncludeTestInterfaces(true) - } + setIncludeTestInterfaces(true) } @After fun tearDown() { - runAsShell(MANAGE_TEST_NETWORKS, NETWORK_SETTINGS) { - em.setIncludeTestInterfaces(false) - for (iface in createdIfaces) { - if (iface.fileDescriptor.fileDescriptor.valid()) iface.fileDescriptor.close() - } - for (listener in addedListeners) { - em.removeInterfaceStateListener(listener) - } + setIncludeTestInterfaces(false) + for (iface in createdIfaces) { + if (iface.fileDescriptor.fileDescriptor.valid()) iface.fileDescriptor.close() + } + for (listener in addedListeners) { + em.removeInterfaceStateListener(listener) } } @@ -175,8 +167,16 @@ class EthernetManagerTest { } private fun createInterface(): TestNetworkInterface { - val tnm = context.getSystemService(TestNetworkManager::class.java) - return tnm.createTapInterface(false /* bringUp */).also { createdIfaces.add(it) } + return runAsShell(MANAGE_TEST_NETWORKS) { + val tnm = context.getSystemService(TestNetworkManager::class.java) + tnm.createTapInterface(false /* bringUp */).also { createdIfaces.add(it) } + } + } + + private fun setIncludeTestInterfaces(value: Boolean) { + runAsShell(NETWORK_SETTINGS) { + em.setIncludeTestInterfaces(value) + } } private fun removeInterface(iface: TestNetworkInterface) { @@ -184,8 +184,9 @@ class EthernetManagerTest { createdIfaces.remove(iface) } - private fun doTestGetInterfaceList() { - em.setIncludeTestInterfaces(true) + @Test + public fun testGetInterfaceList() { + setIncludeTestInterfaces(true) // Create two test interfaces and check the return list contains the interface names. val iface1 = createInterface() @@ -204,11 +205,4 @@ class EthernetManagerTest { removeInterface(iface2) } - - @Test - public fun testGetInterfaceList() { - runAsShell(MANAGE_TEST_NETWORKS, NETWORK_SETTINGS) { - doTestGetInterfaceList() - } - } } From 03d18df93d2aa0e6faad5347bf36f972a9908fb2 Mon Sep 17 00:00:00 2001 From: Patrick Rohr Date: Fri, 8 Apr 2022 15:52:21 +0200 Subject: [PATCH 2/3] Reorder EthernetManagerTest Putting setUp() / tearDown() methods to the top and tests next to each other. Test: atest CtsNetTestCases:EthernetManagerTest Change-Id: I38671aa28f03b8f1de8f6689f17d7c051529d9c5 --- .../android/net/cts/EthernetManagerTest.kt | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt index 56361d8500..05d0beb2ea 100644 --- a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt +++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt @@ -116,35 +116,6 @@ class EthernetManagerTest { } } - @Test - public fun testCallbacks() { - val executor = HandlerExecutor(Handler(Looper.getMainLooper())) - - // If an interface exists when the callback is registered, it is reported on registration. - val iface = createInterface() - val listener = EthernetStateListener() - addInterfaceStateListener(executor, listener) - listener.expectCallback(iface, STATE_LINK_UP, ROLE_CLIENT) - - // If an interface appears, existing callbacks see it. - // TODO: fix the up/up/down/up callbacks and only send down/up. - val iface2 = createInterface() - listener.expectCallback(iface2, STATE_LINK_UP, ROLE_CLIENT) - listener.expectCallback(iface2, STATE_LINK_UP, ROLE_CLIENT) - listener.expectCallback(iface2, STATE_LINK_DOWN, ROLE_CLIENT) - listener.expectCallback(iface2, STATE_LINK_UP, ROLE_CLIENT) - - // Removing interfaces first sends link down, then STATE_ABSENT/ROLE_NONE. - removeInterface(iface) - listener.expectCallback(iface, STATE_LINK_DOWN, ROLE_CLIENT) - listener.expectCallback(iface, STATE_ABSENT, ROLE_NONE) - - removeInterface(iface2) - listener.expectCallback(iface2, STATE_LINK_DOWN, ROLE_CLIENT) - listener.expectCallback(iface2, STATE_ABSENT, ROLE_NONE) - listener.assertNoCallback() - } - @Before fun setUp() { setIncludeTestInterfaces(true) @@ -184,6 +155,35 @@ class EthernetManagerTest { createdIfaces.remove(iface) } + @Test + public fun testCallbacks() { + val executor = HandlerExecutor(Handler(Looper.getMainLooper())) + + // If an interface exists when the callback is registered, it is reported on registration. + val iface = createInterface() + val listener = EthernetStateListener() + addInterfaceStateListener(executor, listener) + listener.expectCallback(iface, STATE_LINK_UP, ROLE_CLIENT) + + // If an interface appears, existing callbacks see it. + // TODO: fix the up/up/down/up callbacks and only send down/up. + val iface2 = createInterface() + listener.expectCallback(iface2, STATE_LINK_UP, ROLE_CLIENT) + listener.expectCallback(iface2, STATE_LINK_UP, ROLE_CLIENT) + listener.expectCallback(iface2, STATE_LINK_DOWN, ROLE_CLIENT) + listener.expectCallback(iface2, STATE_LINK_UP, ROLE_CLIENT) + + // Removing interfaces first sends link down, then STATE_ABSENT/ROLE_NONE. + removeInterface(iface) + listener.expectCallback(iface, STATE_LINK_DOWN, ROLE_CLIENT) + listener.expectCallback(iface, STATE_ABSENT, ROLE_NONE) + + removeInterface(iface2) + listener.expectCallback(iface2, STATE_LINK_DOWN, ROLE_CLIENT) + listener.expectCallback(iface2, STATE_ABSENT, ROLE_NONE) + listener.assertNoCallback() + } + @Test public fun testGetInterfaceList() { setIncludeTestInterfaces(true) From dcfe1937d995b849436f40622a4cfc3d9a1dc699 Mon Sep 17 00:00:00 2001 From: Patrick Rohr Date: Mon, 11 Apr 2022 21:28:42 +0200 Subject: [PATCH 3/3] Add support for creating multiple ifaces with RA responders The RA responder is needed in order to properly provision interfaces in the ethernet tests. This CL wraps the tap interface creation and destruction inside a small helper class. Test: atest CtsNetTestCases:EthernetManagerTest Change-Id: Id92a233873f2f8a738ca563f507a2ead4478aebc --- .../android/net/cts/EthernetManagerTest.kt | 64 +++++++++++++++---- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt index 05d0beb2ea..30e0015a1c 100644 --- a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt +++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt @@ -17,7 +17,9 @@ package android.net.cts import android.Manifest.permission.MANAGE_TEST_NETWORKS import android.Manifest.permission.NETWORK_SETTINGS +import android.net.InetAddresses import android.net.IpConfiguration +import android.net.MacAddress import android.net.TestNetworkInterface import android.net.TestNetworkManager import android.platform.test.annotations.AppModeFull @@ -32,6 +34,7 @@ import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test +import android.content.Context import org.junit.runner.RunWith import kotlin.test.assertNull import kotlin.test.fail @@ -46,10 +49,15 @@ import com.android.networkstack.apishim.common.EthernetManagerShim.STATE_LINK_UP import com.android.networkstack.apishim.common.EthernetManagerShim.ROLE_CLIENT import com.android.networkstack.apishim.common.EthernetManagerShim.ROLE_NONE import com.android.networkstack.apishim.EthernetManagerShimImpl +import com.android.testutils.RouterAdvertisementResponder +import com.android.testutils.TapPacketReader +import com.android.testutils.waitForIdle +import java.net.Inet6Address import java.util.concurrent.Executor import kotlin.test.assertFalse import kotlin.test.assertEquals import kotlin.test.assertTrue +import java.net.NetworkInterface private const val TIMEOUT_MS = 1000L private const val NO_CALLBACK_TIMEOUT_MS = 200L @@ -66,9 +74,40 @@ class EthernetManagerTest { private val context by lazy { InstrumentationRegistry.getInstrumentation().context } private val em by lazy { EthernetManagerShimImpl.newInstance(context) } - private val createdIfaces = ArrayList() + private val createdIfaces = ArrayList() private val addedListeners = ArrayList() + private class EthernetTestInterface( + context: Context, + private val handler: Handler + ) { + private val tapInterface: TestNetworkInterface + private val packetReader: TapPacketReader + private val raResponder: RouterAdvertisementResponder + val interfaceName get() = tapInterface.interfaceName + + init { + tapInterface = runAsShell(MANAGE_TEST_NETWORKS) { + val tnm = context.getSystemService(TestNetworkManager::class.java) + tnm.createTapInterface(false /* bringUp */) + } + val mtu = NetworkInterface.getByName(tapInterface.interfaceName).getMTU() + packetReader = TapPacketReader(handler, tapInterface.fileDescriptor.fileDescriptor, mtu) + raResponder = RouterAdvertisementResponder(packetReader) + raResponder.addRouterEntry(MacAddress.fromString("01:23:45:67:89:ab"), + InetAddresses.parseNumericAddress("fe80::abcd") as Inet6Address) + + packetReader.startAsyncForTest() + raResponder.start() + } + + fun destroy() { + raResponder.stop() + handler.post({ packetReader.stop() }) + handler.waitForIdle(TIMEOUT_MS) + } + } + private open class EthernetStateListener private constructor( private val history: ArrayTrackRecord ) : InterfaceStateListener, @@ -101,7 +140,7 @@ class EthernetManagerTest { return event as T } - fun expectCallback(iface: TestNetworkInterface, state: Int, role: Int) { + fun expectCallback(iface: EthernetTestInterface, state: Int, role: Int) { expectCallback(InterfaceStateChanged(iface.interfaceName, state, role, if (state != STATE_ABSENT) DEFAULT_IP_CONFIGURATION else null)) } @@ -125,7 +164,7 @@ class EthernetManagerTest { fun tearDown() { setIncludeTestInterfaces(false) for (iface in createdIfaces) { - if (iface.fileDescriptor.fileDescriptor.valid()) iface.fileDescriptor.close() + iface.destroy() } for (listener in addedListeners) { em.removeInterfaceStateListener(listener) @@ -137,11 +176,8 @@ class EthernetManagerTest { addedListeners.add(listener) } - private fun createInterface(): TestNetworkInterface { - return runAsShell(MANAGE_TEST_NETWORKS) { - val tnm = context.getSystemService(TestNetworkManager::class.java) - tnm.createTapInterface(false /* bringUp */).also { createdIfaces.add(it) } - } + private fun createInterface(): EthernetTestInterface { + return EthernetTestInterface(context, Handler(Looper.getMainLooper())) } private fun setIncludeTestInterfaces(value: Boolean) { @@ -150,8 +186,8 @@ class EthernetManagerTest { } } - private fun removeInterface(iface: TestNetworkInterface) { - iface.fileDescriptor.close() + private fun removeInterface(iface: EthernetTestInterface) { + iface.destroy() createdIfaces.remove(iface) } @@ -193,15 +229,15 @@ class EthernetManagerTest { val iface2 = createInterface() var ifaces = em.getInterfaceList() assertTrue(ifaces.size > 0) - assertTrue(ifaces.contains(iface1.getInterfaceName())) - assertTrue(ifaces.contains(iface2.getInterfaceName())) + assertTrue(ifaces.contains(iface1.interfaceName)) + assertTrue(ifaces.contains(iface2.interfaceName)) // 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.getInterfaceName())) - assertTrue(ifaces.contains(iface2.getInterfaceName())) + assertFalse(ifaces.contains(iface1.interfaceName)) + assertTrue(ifaces.contains(iface2.interfaceName)) removeInterface(iface2) }