Move TapPacketReader into EthernetTestInterface

Test: atest NsdManagerDownstreamTetheringTest
Change-Id: I7a13f8a6d362429c0f59cb9bbffe4a6a59048537
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerDownstreamTetheringTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerDownstreamTetheringTest.kt
index 786023c..24af42b 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerDownstreamTetheringTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerDownstreamTetheringTest.kt
@@ -32,7 +32,6 @@
 import com.android.testutils.DevSdkIgnoreRule
 import com.android.testutils.DevSdkIgnoreRunner
 import com.android.testutils.NsdDiscoveryRecord
-import com.android.testutils.TapPacketReader
 import com.android.testutils.pollForQuery
 import com.android.testutils.tryTest
 import java.util.Random
@@ -59,6 +58,7 @@
     private val handlerThread = HandlerThread("$TAG thread").apply { start() }
     private val handler = Handler(handlerThread.looper)
     private lateinit var downstreamIface: EthernetTestInterface
+    private var tetheringEventCallback: MyTetheringEventCallback? = null
 
     @get:Rule
     val testInterfaceRule = AutoCloseTestInterfaceRule(context)
@@ -77,6 +77,7 @@
         }
         handlerThread.quitSafely()
         handlerThread.join()
+        maybeUnregisterTetheringEventCallback(tetheringEventCallback)
         super.tearDown()
     }
 
@@ -84,9 +85,6 @@
     fun testMdnsDiscoveryCanSendPacketOnLocalOnlyDownstreamTetheringInterface() {
         assumeFalse(isInterfaceForTetheringAvailable())
 
-        var tetheringEventCallback: MyTetheringEventCallback? = null
-        var downstreamReader: TapPacketReader? = null
-
         val discoveryRecord = NsdDiscoveryRecord()
 
         tryTest {
@@ -100,21 +98,15 @@
             ).apply {
                 awaitInterfaceLocalOnly()
             }
-            // This shouldn't be flaky because the TAP interface will buffer all packets even
-            // before the reader is started.
-            downstreamReader = makePacketReader(downstreamIface.testIface)
+            val downstreamReader = downstreamIface.packetReader
             waitForRouterAdvertisement(downstreamReader, iface, WAIT_RA_TIMEOUT_MS)
 
             nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, discoveryRecord)
             discoveryRecord.expectCallback<NsdDiscoveryRecord.DiscoveryEvent.DiscoveryStarted>()
-            assertNotNull(downstreamReader?.pollForQuery("$serviceType.local", 12 /* type PTR */))
+            assertNotNull(downstreamReader.pollForQuery("$serviceType.local", 12 /* type PTR */))
         } cleanupStep {
             nsdManager.stopServiceDiscovery(discoveryRecord)
             discoveryRecord.expectCallback<NsdDiscoveryRecord.DiscoveryEvent.DiscoveryStopped>()
-        } cleanupStep {
-            maybeStopTapPacketReader(downstreamReader)
-        } cleanup {
-            maybeUnregisterTetheringEventCallback(tetheringEventCallback)
         }
     }
 
@@ -122,9 +114,6 @@
     fun testMdnsDiscoveryWorkOnTetheringInterface() {
         assumeFalse(isInterfaceForTetheringAvailable())
 
-        var tetheringEventCallback: MyTetheringEventCallback? = null
-        var downstreamReader: TapPacketReader? = null
-
         val discoveryRecord = NsdDiscoveryRecord()
 
         tryTest {
@@ -143,21 +132,14 @@
                 awaitInterfaceTethered()
             }
 
-            val fd = downstreamIface.testIface.fileDescriptor?.fileDescriptor
-            assertNotNull(fd)
-            downstreamReader = makePacketReader(fd, getMTU(downstreamIface.testIface))
-
             nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, discoveryRecord)
             discoveryRecord.expectCallback<NsdDiscoveryRecord.DiscoveryEvent.DiscoveryStarted>()
-            assertNotNull(downstreamReader?.pollForQuery("$serviceType.local", 12 /* type PTR */))
+            val downstreamReader = downstreamIface.packetReader
+            assertNotNull(downstreamReader.pollForQuery("$serviceType.local", 12 /* type PTR */))
             // TODO: Add another test to check packet reply can trigger serviceFound.
         } cleanupStep {
             nsdManager.stopServiceDiscovery(discoveryRecord)
             discoveryRecord.expectCallback<NsdDiscoveryRecord.DiscoveryEvent.DiscoveryStopped>()
-        } cleanupStep {
-            maybeStopTapPacketReader(downstreamReader)
-        } cleanup {
-            maybeUnregisterTetheringEventCallback(tetheringEventCallback)
         }
     }
 }
diff --git a/tests/cts/net/util/java/android/net/cts/util/EthernetTestInterface.kt b/tests/cts/net/util/java/android/net/cts/util/EthernetTestInterface.kt
index b06245b..32d6899 100644
--- a/tests/cts/net/util/java/android/net/cts/util/EthernetTestInterface.kt
+++ b/tests/cts/net/util/java/android/net/cts/util/EthernetTestInterface.kt
@@ -28,7 +28,10 @@
 import android.os.Handler
 import android.util.Log
 import com.android.net.module.util.ArrayTrackRecord
+import com.android.testutils.TapPacketReader
 import com.android.testutils.runAsShell
+import com.android.testutils.waitForIdle
+import java.net.NetworkInterface
 import kotlin.concurrent.Volatile
 import kotlin.test.assertNotNull
 
@@ -76,6 +79,13 @@
     }
 
     val name get() = testIface.interfaceName
+    val mtu: Int
+        get() {
+            val nif = NetworkInterface.getByName(name)
+            assertNotNull(nif)
+            return nif.mtu
+        }
+    val packetReader = TapPacketReader(handler, testIface.fileDescriptor.fileDescriptor, mtu)
     private val listener = EthernetStateListener(name)
     private val em = context.getSystemService(EthernetManager::class.java)!!
     @Volatile private var cleanedUp = false
@@ -87,11 +97,14 @@
         }
         // Wait for link up to be processed in EthernetManager before returning.
         listener.eventuallyExpect(STATE_LINK_UP)
+        handler.post { packetReader.start() }
+        handler.waitForIdle(TIMEOUT_MS)
     }
 
     fun destroy() {
-        // It is possible that the fd was already closed by the test, in which case this is a noop.
-        testIface.getFileDescriptor().close()
+        // packetReader.stop() closes the test interface.
+        handler.post { packetReader.stop() }
+        handler.waitForIdle(TIMEOUT_MS)
         listener.eventuallyExpect(STATE_ABSENT)
 
         // setIncludeTestInterfaces() posts on the handler and does not run synchronously. However,