blob: 3319fbe1975fd94b4f9c978aba7ce42e44ef8de5 [file] [log] [blame]
/*
* 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()
}
}
}
}