Merge "Add visibility of this lib to wifi tests"
diff --git a/staticlibs/devicetests/com/android/testutils/TapPacketReader.java b/staticlibs/devicetests/com/android/testutils/TapPacketReader.java
index c36f144..001b709 100644
--- a/staticlibs/devicetests/com/android/testutils/TapPacketReader.java
+++ b/staticlibs/devicetests/com/android/testutils/TapPacketReader.java
@@ -50,6 +50,17 @@
         mTapFd = tapFd;
     }
 
+
+    /**
+     * Attempt to start the FdEventsReader on its handler thread.
+     *
+     * As opposed to {@link android.net.util.FdEventsReader#start()}, this method will not report
+     * failure to start, so it is only appropriate in tests that will fail later if that happens.
+     */
+    public void startAsyncForTest() {
+        getHandler().post(this::start);
+    }
+
     @Override
     protected FileDescriptor createFd() {
         return mTapFd;
@@ -64,10 +75,30 @@
     }
 
     /**
+     * @deprecated This method does not actually "pop" (which generally means the last packet).
+     * Use {@link #poll(long)}, which has the same behavior, instead.
+     */
+    @Nullable
+    @Deprecated
+    public byte[] popPacket(long timeoutMs) {
+        return poll(timeoutMs);
+    }
+
+    /**
+     * @deprecated This method does not actually "pop" (which generally means the last packet).
+     * Use {@link #poll(long, Predicate)}, which has the same behavior, instead.
+     */
+    @Nullable
+    @Deprecated
+    public byte[] popPacket(long timeoutMs, @NonNull Predicate<byte[]> filter) {
+        return poll(timeoutMs, filter);
+    }
+
+    /**
      * Get the next packet that was received on the interface.
      */
     @Nullable
-    public byte[] popPacket(long timeoutMs) {
+    public byte[] poll(long timeoutMs) {
         return mReadHead.getValue().poll(timeoutMs, packet -> true);
     }
 
@@ -75,7 +106,7 @@
      * Get the next packet that was received on the interface and matches the specified filter.
      */
     @Nullable
-    public byte[] popPacket(long timeoutMs, @NonNull Predicate<byte[]> filter) {
+    public byte[] poll(long timeoutMs, @NonNull Predicate<byte[]> filter) {
         return mReadHead.getValue().poll(timeoutMs, filter::test);
     }
 
diff --git a/staticlibs/devicetests/com/android/testutils/TapPacketReaderRule.kt b/staticlibs/devicetests/com/android/testutils/TapPacketReaderRule.kt
new file mode 100644
index 0000000..3319fbe
--- /dev/null
+++ b/staticlibs/devicetests/com/android/testutils/TapPacketReaderRule.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.testutils
+
+import android.Manifest.permission.MANAGE_TEST_NETWORKS
+import android.net.TestNetworkInterface
+import android.net.TestNetworkManager
+import android.os.HandlerThread
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+import kotlin.test.assertFalse
+import kotlin.test.fail
+
+private const val HANDLER_TIMEOUT_MS = 10_000L
+
+/**
+ * A [TestRule] that sets up a [TapPacketReader] on a [TestNetworkInterface] for use in the test.
+ */
+class TapPacketReaderRule @JvmOverloads constructor(
+    private val maxPacketSize: Int = 1500
+) : TestRule {
+    // Use lateinit as the below members can't be initialized in the rule constructor (the
+    // InstrumentationRegistry may not be ready), but from the point of view of test cases using
+    // this rule, the members are always initialized (in setup/test/teardown): tests cases should be
+    // able use them directly.
+    // lateinit also allows getting good exceptions detailing what went wrong in the unlikely event
+    // that the members are referenced before they could be initialized.
+    lateinit var iface: TestNetworkInterface
+    lateinit var reader: TapPacketReader
+
+    // The reader runs on its own handlerThread created locally, but this is not an actual
+    // requirement: any handler could be used for this rule. If using a specific handler is needed,
+    // a method could be added to start the TapPacketReader manually on a given handler.
+    private val handlerThread = HandlerThread(TapPacketReaderRule::class.java.simpleName)
+
+    override fun apply(base: Statement, description: Description): Statement {
+        return TapReaderStatement(base)
+    }
+
+    private inner class TapReaderStatement(private val base: Statement) : Statement() {
+        override fun evaluate() {
+            val ctx: android.content.Context = InstrumentationRegistry.getInstrumentation().context
+            iface = runAsShell(MANAGE_TEST_NETWORKS) {
+                val tnm = ctx.getSystemService(TestNetworkManager::class.java)
+                        ?: fail("Could not obtain the TestNetworkManager")
+                tnm.createTapInterface()
+            }
+
+            handlerThread.start()
+            reader = TapPacketReader(handlerThread.threadHandler,
+                    iface.fileDescriptor.fileDescriptor, maxPacketSize)
+            reader.startAsyncForTest()
+
+            try {
+                base.evaluate()
+            } finally {
+                handlerThread.threadHandler.post(reader::stop)
+                handlerThread.quitSafely()
+                handlerThread.join(HANDLER_TIMEOUT_MS)
+                assertFalse(handlerThread.isAlive,
+                        "HandlerThread did not exit within $HANDLER_TIMEOUT_MS ms")
+                iface.fileDescriptor.close()
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/staticlibs/devicetests/com/android/testutils/TestPermissionUtil.kt b/staticlibs/devicetests/com/android/testutils/TestPermissionUtil.kt
new file mode 100644
index 0000000..f557f18
--- /dev/null
+++ b/staticlibs/devicetests/com/android/testutils/TestPermissionUtil.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:JvmName("TestPermissionUtil")
+
+package com.android.testutils
+
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.testutils.ExceptionUtils.ThrowingRunnable
+import com.android.testutils.ExceptionUtils.ThrowingSupplier
+
+/**
+ * Run the specified [task] with the specified [permissions] obtained through shell
+ * permission identity.
+ *
+ * Passing in an empty list of permissions can grant all shell permissions, but this is
+ * discouraged as it also causes the process to temporarily lose non-shell permissions.
+ */
+fun <T> runAsShell(vararg permissions: String, task: () -> T): T {
+    val autom = InstrumentationRegistry.getInstrumentation().uiAutomation
+    autom.adoptShellPermissionIdentity(*permissions)
+    try {
+        return task()
+    } finally {
+        autom.dropShellPermissionIdentity()
+    }
+}
+
+/**
+ * Convenience overload of [runAsShell] that uses a [ThrowingSupplier] for Java callers, when
+ * only one/two/three permissions are needed.
+ */
+@JvmOverloads
+fun <T> runAsShell(
+    perm1: String,
+    perm2: String = "",
+    perm3: String = "",
+    supplier: ThrowingSupplier<T>
+): T = runAsShell(*getNonEmptyVarargs(perm1, perm2, perm3)) { supplier.get() }
+
+/**
+ * Convenience overload of [runAsShell] that uses a [ThrowingRunnable] for Java callers, when
+ * only one/two/three permissions are needed.
+ */
+@JvmOverloads
+fun runAsShell(
+    perm1: String,
+    perm2: String = "",
+    perm3: String = "",
+    runnable: ThrowingRunnable
+): Unit = runAsShell(*getNonEmptyVarargs(perm1, perm2, perm3)) { runnable.run() }
+
+/**
+ * Get an array containing the first consecutive non-empty arguments out of three arguments.
+ *
+ * The first argument is assumed to be non-empty.
+ */
+private fun getNonEmptyVarargs(arg1: String, arg2: String, arg3: String): Array<String> {
+    return when {
+        arg2 == "" -> arrayOf(arg1)
+        arg3 == "" -> arrayOf(arg1, arg2)
+        else -> arrayOf(arg1, arg2, arg3)
+    }
+}
\ No newline at end of file