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