summaryrefslogtreecommitdiff
path: root/common/testutils/devicetests/com/android
diff options
context:
space:
mode:
Diffstat (limited to 'common/testutils/devicetests/com/android')
-rw-r--r--common/testutils/devicetests/com/android/testutils/ArpResponder.kt64
-rw-r--r--common/testutils/devicetests/com/android/testutils/CompatUtil.kt53
-rw-r--r--common/testutils/devicetests/com/android/testutils/ConcurrentInterpreter.kt240
-rw-r--r--common/testutils/devicetests/com/android/testutils/ConnectUtil.kt203
-rw-r--r--common/testutils/devicetests/com/android/testutils/ContextUtils.kt67
-rw-r--r--common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt144
-rw-r--r--common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRunner.kt91
-rw-r--r--common/testutils/devicetests/com/android/testutils/DeviceConfigRule.kt176
-rw-r--r--common/testutils/devicetests/com/android/testutils/DeviceInfoUtils.java176
-rw-r--r--common/testutils/devicetests/com/android/testutils/DnsAnswerProvider.kt65
-rw-r--r--common/testutils/devicetests/com/android/testutils/DumpTestUtils.java128
-rw-r--r--common/testutils/devicetests/com/android/testutils/FakeDns.kt94
-rw-r--r--common/testutils/devicetests/com/android/testutils/HandlerUtils.kt90
-rw-r--r--common/testutils/devicetests/com/android/testutils/NatExternalPacketForwarder.kt81
-rw-r--r--common/testutils/devicetests/com/android/testutils/NatInternalPacketForwarder.kt78
-rw-r--r--common/testutils/devicetests/com/android/testutils/NatPacketForwarderBase.java207
-rw-r--r--common/testutils/devicetests/com/android/testutils/NetlinkTestUtils.kt108
-rw-r--r--common/testutils/devicetests/com/android/testutils/NetworkStatsProviderCbStubCompat.java46
-rw-r--r--common/testutils/devicetests/com/android/testutils/NetworkStatsProviderStubCompat.java37
-rw-r--r--common/testutils/devicetests/com/android/testutils/NetworkStatsUtils.kt78
-rw-r--r--common/testutils/devicetests/com/android/testutils/NonNullTestUtils.java33
-rw-r--r--common/testutils/devicetests/com/android/testutils/PacketBridge.kt173
-rw-r--r--common/testutils/devicetests/com/android/testutils/PacketReflector.java264
-rw-r--r--common/testutils/devicetests/com/android/testutils/PacketReflectorUtil.kt114
-rw-r--r--common/testutils/devicetests/com/android/testutils/PacketResponder.kt75
-rw-r--r--common/testutils/devicetests/com/android/testutils/ParcelUtils.kt70
-rw-r--r--common/testutils/devicetests/com/android/testutils/RouterAdvertisementResponder.java208
-rw-r--r--common/testutils/devicetests/com/android/testutils/TapPacketReader.java137
-rw-r--r--common/testutils/devicetests/com/android/testutils/TapPacketReaderRule.kt154
-rw-r--r--common/testutils/devicetests/com/android/testutils/TestBpfMap.java139
-rw-r--r--common/testutils/devicetests/com/android/testutils/TestDnsServer.kt173
-rw-r--r--common/testutils/devicetests/com/android/testutils/TestHttpServer.kt105
-rw-r--r--common/testutils/devicetests/com/android/testutils/TestNetworkTracker.kt158
-rw-r--r--common/testutils/devicetests/com/android/testutils/TestPermissionUtil.kt95
-rw-r--r--common/testutils/devicetests/com/android/testutils/TestableNetworkAgent.kt206
-rw-r--r--common/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt569
-rw-r--r--common/testutils/devicetests/com/android/testutils/TestableNetworkOfferCallback.kt75
-rw-r--r--common/testutils/devicetests/com/android/testutils/TestableNetworkStatsProvider.kt123
-rw-r--r--common/testutils/devicetests/com/android/testutils/TestableNetworkStatsProviderBinder.kt68
-rw-r--r--common/testutils/devicetests/com/android/testutils/TestableNetworkStatsProviderCbBinder.kt100
-rw-r--r--common/testutils/devicetests/com/android/testutils/async/FakeOsAccess.java568
-rw-r--r--common/testutils/devicetests/com/android/testutils/async/RateLimiter.java131
-rw-r--r--common/testutils/devicetests/com/android/testutils/async/ReadableDataAnswer.java76
-rw-r--r--common/testutils/devicetests/com/android/testutils/filters/CtsNetTestCasesMaxTargetSdk30.kt22
-rw-r--r--common/testutils/devicetests/com/android/testutils/filters/CtsNetTestCasesMaxTargetSdk31.kt22
-rw-r--r--common/testutils/devicetests/com/android/testutils/filters/CtsNetTestCasesMaxTargetSdk33.kt22
46 files changed, 0 insertions, 6106 deletions
diff --git a/common/testutils/devicetests/com/android/testutils/ArpResponder.kt b/common/testutils/devicetests/com/android/testutils/ArpResponder.kt
deleted file mode 100644
index cf0490c5..00000000
--- a/common/testutils/devicetests/com/android/testutils/ArpResponder.kt
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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.net.MacAddress
-import com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN
-import java.net.Inet4Address
-import java.net.InetAddress
-import java.nio.ByteBuffer
-
-private const val ARP_SENDER_MAC_OFFSET = ETHER_HEADER_LEN + 8
-private const val ARP_TARGET_IPADDR_OFFSET = ETHER_HEADER_LEN + 24
-
-private val TYPE_ARP = byteArrayOf(0x08, 0x06)
-// Arp reply header for IPv4 over ethernet
-private val ARP_REPLY_IPV4 = byteArrayOf(0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x02)
-
-/**
- * A class that can be used to reply to ARP packets on a [TapPacketReader].
- */
-class ArpResponder(
- reader: TapPacketReader,
- table: Map<Inet4Address, MacAddress>,
- name: String = ArpResponder::class.java.simpleName
-) : PacketResponder(reader, ArpRequestFilter(), name) {
- // Copy the map if not already immutable (toMap) to make sure it is not modified
- private val table = table.toMap()
-
- override fun replyToPacket(packet: ByteArray, reader: TapPacketReader) {
- val targetIp = InetAddress.getByAddress(
- packet.copyFromIndexWithLength(ARP_TARGET_IPADDR_OFFSET, 4))
- as Inet4Address
-
- val macAddr = table[targetIp]?.toByteArray() ?: return
- val senderMac = packet.copyFromIndexWithLength(ARP_SENDER_MAC_OFFSET, 6)
- reader.sendResponse(ByteBuffer.wrap(
- // Ethernet header
- senderMac + macAddr + TYPE_ARP +
- // ARP message
- ARP_REPLY_IPV4 +
- macAddr /* sender MAC */ +
- targetIp.address /* sender IP addr */ +
- macAddr /* target mac */ +
- targetIp.address /* target IP addr */
- ))
- }
-}
-
-private fun ByteArray.copyFromIndexWithLength(start: Int, len: Int) =
- copyOfRange(start, start + len)
diff --git a/common/testutils/devicetests/com/android/testutils/CompatUtil.kt b/common/testutils/devicetests/com/android/testutils/CompatUtil.kt
deleted file mode 100644
index 82f1d9b9..00000000
--- a/common/testutils/devicetests/com/android/testutils/CompatUtil.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2021 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.net.NetworkSpecifier
-import com.android.modules.utils.build.SdkLevel.isAtLeastS
-
-/**
- * Test utility to create [NetworkSpecifier]s on different SDK versions.
- */
-object CompatUtil {
- @JvmStatic
- fun makeTestNetworkSpecifier(ifName: String): NetworkSpecifier {
- // Until R, there was no TestNetworkSpecifier, StringNetworkSpecifier was used instead
- if (!isAtLeastS()) {
- return makeNetworkSpecifierInternal("android.net.StringNetworkSpecifier", ifName)
- }
- // TestNetworkSpecifier is not part of the SDK in some branches using this utility
- // TODO: replace with a direct call to the constructor
- return makeNetworkSpecifierInternal("android.net.TestNetworkSpecifier", ifName)
- }
-
- @JvmStatic
- fun makeEthernetNetworkSpecifier(ifName: String): NetworkSpecifier {
- // Until R, there was no EthernetNetworkSpecifier, StringNetworkSpecifier was used instead
- if (!isAtLeastS()) {
- return makeNetworkSpecifierInternal("android.net.StringNetworkSpecifier", ifName)
- }
- // EthernetNetworkSpecifier is not part of the SDK in some branches using this utility
- // TODO: replace with a direct call to the constructor
- return makeNetworkSpecifierInternal("android.net.EthernetNetworkSpecifier", ifName)
- }
-
- private fun makeNetworkSpecifierInternal(clazz: String, specifier: String): NetworkSpecifier {
- // StringNetworkSpecifier was removed after R (and was hidden API before that)
- return Class.forName(clazz)
- .getConstructor(String::class.java).newInstance(specifier) as NetworkSpecifier
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/ConcurrentInterpreter.kt b/common/testutils/devicetests/com/android/testutils/ConcurrentInterpreter.kt
deleted file mode 100644
index 9e72f4b4..00000000
--- a/common/testutils/devicetests/com/android/testutils/ConcurrentInterpreter.kt
+++ /dev/null
@@ -1,240 +0,0 @@
-package com.android.testutils
-
-import android.os.SystemClock
-import java.util.concurrent.CyclicBarrier
-import kotlin.test.assertEquals
-import kotlin.test.assertFails
-import kotlin.test.assertNull
-import kotlin.test.assertTrue
-
-// The table contains pairs associating a regexp with the code to run. The statement is matched
-// against each matcher in sequence and when a match is found the associated code is run, passing
-// it the TrackRecord under test and the result of the regexp match.
-typealias InterpretMatcher<T> = Pair<Regex, (ConcurrentInterpreter<T>, T, MatchResult) -> Any?>
-
-// The default unit of time for interpreted tests
-const val INTERPRET_TIME_UNIT = 60L // ms
-
-/**
- * A small interpreter for testing parallel code.
- *
- * The interpreter will read a list of lines consisting of "|"-separated statements, e.g. :
- * sleep 2 ; unblock thread2 | wait thread2 time 2..5
- * sendMessage "x" | obtainMessage = "x" time 0..1
- *
- * Each column runs in a different concurrent thread and all threads wait for each other in
- * between lines. Each statement is split on ";" then matched with regular expressions in the
- * instructionTable constant, which contains the code associated with each statement. The
- * interpreter supports an object being passed to the interpretTestSpec() method to be passed
- * in each lambda (think about the object under test), and an optional transform function to be
- * executed on the object at the start of every thread.
- *
- * The time unit is defined in milliseconds by the interpretTimeUnit member, which has a default
- * value but can be passed to the constructor. Whitespace is ignored.
- *
- * The interpretation table has to be passed as an argument. It's a table associating a regexp
- * with the code that should execute, as a function taking three arguments : the interpreter,
- * the regexp match, and the object. See the individual tests for the DSL of that test.
- * Implementors for new interpreting languages are encouraged to look at the defaultInterpretTable
- * constant below for an example of how to write an interpreting table.
- * Some expressions already exist by default and can be used by all interpreters. Refer to
- * getDefaultInstructions() below for a list and documentation.
- */
-open class ConcurrentInterpreter<T>(localInterpretTable: List<InterpretMatcher<T>>) {
- private val interpretTable: List<InterpretMatcher<T>> =
- localInterpretTable + getDefaultInstructions()
- // The last time the thread became blocked, with base System.currentTimeMillis(). This should
- // be set immediately before any time the thread gets blocked.
- internal val lastBlockedTime = ThreadLocal<Long>()
-
- // Split the line into multiple statements separated by ";" and execute them. Return whatever
- // the last statement returned.
- fun interpretMultiple(instr: String, r: T): Any? {
- return instr.split(";").map { interpret(it.trim(), r) }.last()
- }
-
- // Match the statement to a regex and interpret it.
- fun interpret(instr: String, r: T): Any? {
- val (matcher, code) =
- interpretTable.find { instr matches it.first } ?: throw SyntaxException(instr)
- val match = matcher.matchEntire(instr) ?: throw SyntaxException(instr)
- return code(this, r, match)
- }
-
- /**
- * Spins as many threads as needed by the test spec and interpret each program concurrently.
- *
- * All threads wait on a CyclicBarrier after each line.
- * |lineShift| says how many lines after the call the spec starts. This is used for error
- * reporting. Unfortunately AFAICT there is no way to get the line of an argument rather
- * than the line at which the expression starts.
- *
- * This method is mostly meant for implementations that extend the ConcurrentInterpreter
- * class to add their own directives and instructions. These may need to operate on some
- * data, which can be passed in |initial|. For example, an interpreter specialized in callbacks
- * may want to pass the callback there. In some cases, it's necessary that each thread
- * performs a transformation *after* it starts on that value before starting ; in this case,
- * the transformation can be passed to |threadTransform|. The default is to return |initial| as
- * is. Look at some existing child classes of this interpreter for some examples of how this
- * can be used.
- *
- * @param spec The test spec, as a string of lines separated by pipes.
- * @param initial An initial value passed to all threads.
- * @param lineShift How many lines after the call the spec starts, for error reporting.
- * @param threadTransform an optional transformation that each thread will apply to |initial|
- */
- fun interpretTestSpec(
- spec: String,
- initial: T,
- lineShift: Int = 0,
- threadTransform: (T) -> T = { it }
- ) {
- // For nice stack traces
- val callSite = getCallingMethod()
- val lines = spec.trim().trim('\n').split("\n").map { it.split("|") }
- // |lines| contains arrays of strings that make up the statements of a thread : in other
- // words, it's an array that contains a list of statements for each column in the spec.
- // E.g. if the string is """
- // a | b | c
- // d | e | f
- // """, then lines is [ [ "a", "b", "c" ], [ "d", "e", "f" ] ].
- val threadCount = lines[0].size
- assertTrue(lines.all { it.size == threadCount })
- val threadInstructions = (0 until threadCount).map { i -> lines.map { it[i].trim() } }
- // |threadInstructions| is a list where each element is the list of instructions for the
- // thread at the index. In other words, it's just |lines| transposed. In the example
- // above, it would be [ [ "a", "d" ], [ "b", "e" ], [ "c", "f" ] ]
- // mapIndexed below will pass in |instructions| the list of instructions for this thread.
- val barrier = CyclicBarrier(threadCount)
- var crash: InterpretException? = null
- threadInstructions.mapIndexed { threadIndex, instructions ->
- Thread {
- val threadLocal = threadTransform(initial)
- lastBlockedTime.set(System.currentTimeMillis())
- barrier.await()
- var lineNum = 0
- instructions.forEach {
- if (null != crash) return@Thread
- lineNum += 1
- try {
- interpretMultiple(it, threadLocal)
- } catch (e: Throwable) {
- // If fail() or some exception was called, the thread will come here ; if
- // the exception isn't caught the process will crash, which is not nice for
- // testing. Instead, catch the exception, cancel other threads, and report
- // nicely. Catch throwable because fail() is AssertionError, which inherits
- // from Error.
- crash = InterpretException(threadIndex, it,
- callSite.lineNumber + lineNum + lineShift,
- callSite.className, callSite.methodName, callSite.fileName, e)
- }
- lastBlockedTime.set(System.currentTimeMillis())
- barrier.await()
- }
- }.also { it.start() }
- }.forEach { it.join() }
- // If the test failed, crash with line number
- crash?.let { throw it }
- }
-
- // Helper to get the stack trace for a calling method
- private fun getCallingStackTrace(): Array<StackTraceElement> {
- try {
- throw RuntimeException()
- } catch (e: RuntimeException) {
- return e.stackTrace
- }
- }
-
- // Find the calling method. This is the first method in the stack trace that is annotated
- // with @Test.
- fun getCallingMethod(): StackTraceElement {
- val stackTrace = getCallingStackTrace()
- return stackTrace.find { element ->
- val clazz = Class.forName(element.className)
- // Because the stack trace doesn't list the formal arguments, find all methods with
- // this name and return this name if any of them is annotated with @Test.
- clazz.declaredMethods
- .filter { method -> method.name == element.methodName }
- .any { method -> method.getAnnotation(org.junit.Test::class.java) != null }
- } ?: stackTrace[3]
- // If no method is annotated return the 4th one, because that's what it usually is :
- // 0 is getCallingStackTrace, 1 is this method, 2 is ConcurrentInterpreter#interpretTestSpec
- }
-}
-
-/**
- * Default instructions available to all interpreters.
- * sleep(x) : sleeps for x time units and returns Unit ; sleep alone means sleep(1)
- * EXPR = VALUE : asserts that EXPR equals VALUE. EXPR is interpreted. VALUE can either be the
- * string "null" or an int. Returns Unit.
- * EXPR time x..y : measures the time taken by EXPR and asserts it took at least x and at most
- * y time units.
- * EXPR // any text : comments are ignored.
- * EXPR fails : checks that EXPR throws some exception.
- */
-private fun <T> getDefaultInstructions() = listOf<InterpretMatcher<T>>(
- // Interpret an empty line as doing nothing.
- Regex("") to { _, _, _ -> null },
- // Ignore comments.
- Regex("(.*)//.*") to { i, t, r -> i.interpret(r.strArg(1), t) },
- // Interpret "XXX time x..y" : run XXX and check it took at least x and not more than y
- Regex("""(.*)\s*time\s*(\d+)\.\.(\d+)""") to { i, t, r ->
- val lateStart = System.currentTimeMillis()
- i.interpret(r.strArg(1), t)
- val end = System.currentTimeMillis()
- // There is uncertainty in measuring time.
- // It takes some (small) time for the thread to even measure the time at which it
- // starts interpreting the instruction. It is therefore possible that thread A sleeps for
- // n milliseconds, and B expects to have waited for at least n milliseconds, but because
- // B started measuring after 1ms or so, B thinks it didn't wait long enough.
- // To avoid this, when the `time` instruction tests the instruction took at least X and
- // at most Y, it tests X against a time measured since *before* the thread blocked but
- // Y against a time measured as late as possible. This ensures that the timer is
- // sufficiently lenient in both directions that there are no flaky measures.
- val minTime = end - lateStart
- val maxTime = end - i.lastBlockedTime.get()!!
-
- assertTrue(maxTime >= r.timeArg(2),
- "Should have taken at least ${r.timeArg(2)} but took less than $maxTime")
- assertTrue(minTime <= r.timeArg(3),
- "Should have taken at most ${r.timeArg(3)} but took more than $minTime")
- },
- // Interpret "XXX = YYY" : run XXX and assert its return value is equal to YYY. "null" supported
- Regex("""(.*)\s*=\s*(null|\d+)""") to { i, t, r ->
- i.interpret(r.strArg(1), t).also {
- if ("null" == r.strArg(2)) assertNull(it) else assertEquals(r.intArg(2), it)
- }
- },
- // Interpret sleep. Optional argument for the count, in INTERPRET_TIME_UNIT units.
- Regex("""sleep(\((\d+)\))?""") to { i, t, r ->
- SystemClock.sleep(if (r.strArg(2).isEmpty()) INTERPRET_TIME_UNIT else r.timeArg(2))
- },
- Regex("""(.*)\s*fails""") to { i, t, r ->
- assertFails { i.interpret(r.strArg(1), t) }
- }
-)
-
-class SyntaxException(msg: String, cause: Throwable? = null) : RuntimeException(msg, cause)
-class InterpretException(
- threadIndex: Int,
- instr: String,
- lineNum: Int,
- className: String,
- methodName: String,
- fileName: String,
- cause: Throwable
-) : RuntimeException("Failure: $instr", cause) {
- init {
- stackTrace = arrayOf(StackTraceElement(
- className,
- "$methodName:thread$threadIndex",
- fileName,
- lineNum)) + super.getStackTrace()
- }
-}
-
-// Some small helpers to avoid to say the large ".groupValues[index].trim()" every time
-fun MatchResult.strArg(index: Int) = this.groupValues[index].trim()
-fun MatchResult.intArg(index: Int) = strArg(index).toInt()
-fun MatchResult.timeArg(index: Int) = INTERPRET_TIME_UNIT * intArg(index)
diff --git a/common/testutils/devicetests/com/android/testutils/ConnectUtil.kt b/common/testutils/devicetests/com/android/testutils/ConnectUtil.kt
deleted file mode 100644
index 71f7877e..00000000
--- a/common/testutils/devicetests/com/android/testutils/ConnectUtil.kt
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2021 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
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import android.net.ConnectivityManager
-import android.net.Network
-import android.net.NetworkCapabilities.TRANSPORT_WIFI
-import android.net.NetworkRequest
-import android.net.wifi.ScanResult
-import android.net.wifi.WifiConfiguration
-import android.net.wifi.WifiManager
-import android.os.ParcelFileDescriptor
-import android.os.SystemClock
-import android.util.Log
-import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
-import com.android.testutils.RecorderCallback.CallbackEntry
-import java.util.concurrent.CompletableFuture
-import java.util.concurrent.TimeUnit
-import kotlin.test.assertNotNull
-import kotlin.test.assertTrue
-import kotlin.test.fail
-
-private const val MAX_WIFI_CONNECT_RETRIES = 10
-private const val WIFI_CONNECT_INTERVAL_MS = 500L
-private const val WIFI_CONNECT_TIMEOUT_MS = 30_000L
-
-// Constants used by WifiManager.ActionListener#onFailure. Although onFailure is SystemApi,
-// the error code constants are not (b/204277752)
-private const val WIFI_ERROR_IN_PROGRESS = 1
-private const val WIFI_ERROR_BUSY = 2
-
-class ConnectUtil(private val context: Context) {
- private val TAG = ConnectUtil::class.java.simpleName
-
- private val cm = context.getSystemService(ConnectivityManager::class.java)
- ?: fail("Could not find ConnectivityManager")
- private val wifiManager = context.getSystemService(WifiManager::class.java)
- ?: fail("Could not find WifiManager")
-
- fun ensureWifiConnected(): Network {
- val callback = TestableNetworkCallback()
- cm.registerNetworkCallback(NetworkRequest.Builder()
- .addTransportType(TRANSPORT_WIFI)
- .build(), callback)
-
- try {
- val connInfo = wifiManager.connectionInfo
- Log.d(TAG, "connInfo=" + connInfo)
- if (connInfo == null || connInfo.networkId == -1) {
- clearWifiBlocklist()
- val pfd = getInstrumentation().uiAutomation.executeShellCommand("svc wifi enable")
- // Read the output stream to ensure the command has completed
- ParcelFileDescriptor.AutoCloseInputStream(pfd).use { it.readBytes() }
- val config = getOrCreateWifiConfiguration()
- connectToWifiConfig(config)
- }
- val cb = callback.poll(WIFI_CONNECT_TIMEOUT_MS) { it is CallbackEntry.Available }
- assertNotNull(cb, "Could not connect to a wifi access point within " +
- "$WIFI_CONNECT_TIMEOUT_MS ms. Check that the test device has a wifi network " +
- "configured, and that the test access point is functioning properly.")
- return cb.network
- } finally {
- cm.unregisterNetworkCallback(callback)
- }
- }
-
- private fun connectToWifiConfig(config: WifiConfiguration) {
- repeat(MAX_WIFI_CONNECT_RETRIES) {
- val error = runAsShell(permission.NETWORK_SETTINGS) {
- val listener = ConnectWifiListener()
- wifiManager.connect(config, listener)
- listener.connectFuture.get(WIFI_CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS)
- } ?: return // Connect succeeded
-
- // Only retry for IN_PROGRESS and BUSY
- if (error != WIFI_ERROR_IN_PROGRESS && error != WIFI_ERROR_BUSY) {
- fail("Failed to connect to " + config.SSID + ": " + error)
- }
- Log.w(TAG, "connect failed with $error; waiting before retry")
- SystemClock.sleep(WIFI_CONNECT_INTERVAL_MS)
- }
- fail("Failed to connect to ${config.SSID} after $MAX_WIFI_CONNECT_RETRIES retries")
- }
-
- private class ConnectWifiListener : WifiManager.ActionListener {
- /**
- * Future completed when the connect process ends. Provides the error code or null if none.
- */
- val connectFuture = CompletableFuture<Int?>()
- override fun onSuccess() {
- connectFuture.complete(null)
- }
-
- override fun onFailure(reason: Int) {
- connectFuture.complete(reason)
- }
- }
-
- private fun getOrCreateWifiConfiguration(): WifiConfiguration {
- val configs = runAsShell(permission.NETWORK_SETTINGS) {
- wifiManager.getConfiguredNetworks()
- }
- // If no network is configured, add a config for virtual access points if applicable
- if (configs.size == 0) {
- val scanResults = getWifiScanResults()
- val virtualConfig = maybeConfigureVirtualNetwork(scanResults)
- assertNotNull(virtualConfig, "The device has no configured wifi network")
- return virtualConfig
- }
- // No need to add a configuration: there is already one.
- if (configs.size > 1) {
- // For convenience in case of local testing on devices with multiple saved configs,
- // prefer the first configuration that is in range.
- // In actual tests, there should only be one configuration, and it should be usable as
- // assumed by WifiManagerTest.testConnect.
- Log.w(TAG, "Multiple wifi configurations found: " +
- configs.joinToString(", ") { it.SSID })
- val scanResultsList = getWifiScanResults()
- Log.i(TAG, "Scan results: " + scanResultsList.joinToString(", ") {
- "${it.SSID} (${it.level})"
- })
-
- val scanResults = scanResultsList.map { "\"${it.SSID}\"" }.toSet()
- return configs.firstOrNull { scanResults.contains(it.SSID) } ?: configs[0]
- }
- return configs[0]
- }
-
- private fun getWifiScanResults(): List<ScanResult> {
- val scanResultsFuture = CompletableFuture<List<ScanResult>>()
- runAsShell(permission.NETWORK_SETTINGS) {
- val receiver: BroadcastReceiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- scanResultsFuture.complete(wifiManager.scanResults)
- }
- }
- context.registerReceiver(receiver,
- IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION))
- wifiManager.startScan()
- }
- return try {
- scanResultsFuture.get(WIFI_CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS)
- } catch (e: Exception) {
- throw AssertionError("Wifi scan results not received within timeout", e)
- }
- }
-
- /**
- * If a virtual wifi network is detected, add a configuration for that network.
- * TODO(b/158150376): have the test infrastructure add virtual wifi networks when appropriate.
- */
- private fun maybeConfigureVirtualNetwork(scanResults: List<ScanResult>): WifiConfiguration? {
- // Virtual wifi networks used on the emulator and cloud testing infrastructure
- val virtualSsids = listOf("VirtWifi", "AndroidWifi")
- Log.d(TAG, "Wifi scan results: $scanResults")
- val virtualScanResult = scanResults.firstOrNull { virtualSsids.contains(it.SSID) }
- ?: return null
-
- // Only add the virtual configuration if the virtual AP is detected in scans
- val virtualConfig = WifiConfiguration()
- // ASCII SSIDs need to be surrounded by double quotes
- virtualConfig.SSID = "\"${virtualScanResult.SSID}\""
- virtualConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE)
- runAsShell(permission.NETWORK_SETTINGS) {
- val networkId = wifiManager.addNetwork(virtualConfig)
- assertTrue(networkId >= 0)
- assertTrue(wifiManager.enableNetwork(networkId, false /* attemptConnect */))
- }
- return virtualConfig
- }
-
- /**
- * Re-enable wifi networks that were blocked, typically because no internet connection was
- * detected the last time they were connected. This is necessary to make sure wifi can reconnect
- * to them.
- */
- private fun clearWifiBlocklist() {
- runAsShell(permission.NETWORK_SETTINGS, permission.ACCESS_WIFI_STATE) {
- for (cfg in wifiManager.configuredNetworks) {
- assertTrue(wifiManager.enableNetwork(cfg.networkId, false /* attemptConnect */))
- }
- }
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/ContextUtils.kt b/common/testutils/devicetests/com/android/testutils/ContextUtils.kt
deleted file mode 100644
index 936b5682..00000000
--- a/common/testutils/devicetests/com/android/testutils/ContextUtils.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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("ContextUtils")
-
-package com.android.testutils
-
-import android.content.Context
-import android.os.UserHandle
-import org.mockito.AdditionalAnswers.delegatesTo
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.Mockito.doAnswer
-import org.mockito.Mockito.doReturn
-import org.mockito.Mockito.mock
-import java.util.function.BiConsumer
-
-// Helper function so that Java doesn't have to pass a method that returns Unit
-fun mockContextAsUser(context: Context, functor: BiConsumer<Context, UserHandle>? = null) =
- mockContextAsUser(context) { c, h -> functor?.accept(c, h) }
-
-/**
- * Return a context with assigned user and delegate to original context.
- *
- * @param context the mock context to set up createContextAsUser on. After this function
- * is called, client code can call createContextAsUser and expect a context that
- * will return the correct user and userId.
- *
- * @param functor additional code to run on the created context-as-user instances, for example to
- * set up further mocks on these contexts.
- */
-fun mockContextAsUser(context: Context, functor: ((Context, UserHandle) -> Unit)? = null) {
- doAnswer { invocation ->
- val asUserContext = mock(Context::class.java, delegatesTo<Context>(context))
- val user = invocation.arguments[0] as UserHandle
- val userId = user.identifier
- doReturn(user).`when`(asUserContext).user
- doReturn(userId).`when`(asUserContext).userId
- functor?.let { it(asUserContext, user) }
- asUserContext
- }.`when`(context).createContextAsUser(any(UserHandle::class.java), anyInt() /* flags */)
-}
-
-/**
- * Helper function to mock the desired system service.
- *
- * @param context the mock context to set up the getSystemService and getSystemServiceName.
- * @param clazz the system service class that intents to mock.
- * @param service the system service name that intents to mock.
- */
-fun <T> mockService(context: Context, clazz: Class<T>, name: String, service: T) {
- doReturn(service).`when`(context).getSystemService(name)
- doReturn(name).`when`(context).getSystemServiceName(clazz)
-} \ No newline at end of file
diff --git a/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt b/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt
deleted file mode 100644
index 35f22b9e..00000000
--- a/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * 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.os.Build
-import androidx.test.InstrumentationRegistry
-import com.android.modules.utils.build.UnboundedSdkLevel
-import java.util.regex.Pattern
-import org.junit.Assume.assumeTrue
-import org.junit.rules.TestRule
-import org.junit.runner.Description
-import org.junit.runners.model.Statement
-
-@Deprecated("Use Build.VERSION_CODES", ReplaceWith("Build.VERSION_CODES.S_V2"))
-const val SC_V2 = Build.VERSION_CODES.S_V2
-
-private val MAX_TARGET_SDK_ANNOTATION_RE = Pattern.compile("MaxTargetSdk([0-9]+)$")
-private val targetSdk = InstrumentationRegistry.getContext().applicationInfo.targetSdkVersion
-
-private fun isDevSdkInRange(minExclusive: String?, maxInclusive: String?): Boolean {
- return (minExclusive == null || !isAtMost(minExclusive)) &&
- (maxInclusive == null || isAtMost(maxInclusive))
-}
-
-private fun isAtMost(sdkVersionOrCodename: String): Boolean {
- // UnboundedSdkLevel does not support builds < Q, and may stop supporting Q as well since it
- // is intended for mainline modules that are now R+.
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
- // Assume that any codename passed as argument from current code is a more recent build than
- // Q: this util did not exist before Q, and codenames are only used before the corresponding
- // build is finalized. This util could list 28 older codenames to check against (as per
- // ro.build.version.known_codenames in more recent builds), but this does not seem valuable.
- val intVersion = sdkVersionOrCodename.toIntOrNull() ?: return true
- return Build.VERSION.SDK_INT <= intVersion
- }
- return UnboundedSdkLevel.isAtMost(sdkVersionOrCodename)
-}
-
-/**
- * Returns true if the development SDK version of the device is in the provided annotation range.
- *
- * If the device is not using a release SDK, the development SDK differs from
- * [Build.VERSION.SDK_INT], and is indicated by the device codenames; see [UnboundedSdkLevel].
- */
-fun isDevSdkInRange(
- ignoreUpTo: DevSdkIgnoreRule.IgnoreUpTo?,
- ignoreAfter: DevSdkIgnoreRule.IgnoreAfter?
-): Boolean {
- val minExclusive =
- if (ignoreUpTo?.value == 0) ignoreUpTo.codename
- else ignoreUpTo?.value?.toString()
- val maxInclusive =
- if (ignoreAfter?.value == 0) ignoreAfter.codename
- else ignoreAfter?.value?.toString()
- return isDevSdkInRange(minExclusive, maxInclusive)
-}
-
-private fun getMaxTargetSdk(description: Description): Int? {
- return description.annotations.firstNotNullOfOrNull {
- MAX_TARGET_SDK_ANNOTATION_RE.matcher(it.annotationClass.simpleName).let { m ->
- if (m.find()) m.group(1).toIntOrNull() else null
- }
- }
-}
-
-/**
- * A test rule to ignore tests based on the development SDK level.
- *
- * If the device is not using a release SDK, the development SDK is considered to be higher than
- * [Build.VERSION.SDK_INT].
- *
- * @param ignoreClassUpTo Skip all tests in the class if the device dev SDK is <= this codename or
- * SDK level.
- * @param ignoreClassAfter Skip all tests in the class if the device dev SDK is > this codename or
- * SDK level.
- */
-class DevSdkIgnoreRule @JvmOverloads constructor(
- private val ignoreClassUpTo: String? = null,
- private val ignoreClassAfter: String? = null
-) : TestRule {
- /**
- * @param ignoreClassUpTo Skip all tests in the class if the device dev SDK is <= this value.
- * @param ignoreClassAfter Skip all tests in the class if the device dev SDK is > this value.
- */
- @JvmOverloads
- constructor(ignoreClassUpTo: Int?, ignoreClassAfter: Int? = null) : this(
- ignoreClassUpTo?.toString(), ignoreClassAfter?.toString())
-
- override fun apply(base: Statement, description: Description): Statement {
- return IgnoreBySdkStatement(base, description)
- }
-
- /**
- * Ignore the test for any development SDK that is strictly after [value].
- *
- * If the device is not using a release SDK, the development SDK is considered to be higher
- * than [Build.VERSION.SDK_INT].
- */
- annotation class IgnoreAfter(val value: Int = 0, val codename: String = "")
-
- /**
- * Ignore the test for any development SDK that lower than or equal to [value].
- *
- * If the device is not using a release SDK, the development SDK is considered to be higher
- * than [Build.VERSION.SDK_INT].
- */
- annotation class IgnoreUpTo(val value: Int = 0, val codename: String = "")
-
- private inner class IgnoreBySdkStatement(
- private val base: Statement,
- private val description: Description
- ) : Statement() {
- override fun evaluate() {
- val ignoreAfter = description.getAnnotation(IgnoreAfter::class.java)
- val ignoreUpTo = description.getAnnotation(IgnoreUpTo::class.java)
-
- val devSdkMessage = "Skipping test for build ${Build.VERSION.CODENAME} " +
- "with SDK ${Build.VERSION.SDK_INT}"
- assumeTrue(devSdkMessage, isDevSdkInRange(ignoreClassUpTo, ignoreClassAfter))
- assumeTrue(devSdkMessage, isDevSdkInRange(ignoreUpTo, ignoreAfter))
-
- val maxTargetSdk = getMaxTargetSdk(description)
- if (maxTargetSdk != null) {
- assumeTrue("Skipping test, target SDK $targetSdk greater than $maxTargetSdk",
- targetSdk <= maxTargetSdk)
- }
- base.evaluate()
- }
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRunner.kt b/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRunner.kt
deleted file mode 100644
index 2e73666b..00000000
--- a/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRunner.kt
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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 androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter
-import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
-import org.junit.runner.Description
-import org.junit.runner.Runner
-import org.junit.runner.manipulation.Filter
-import org.junit.runner.manipulation.Filterable
-import org.junit.runner.manipulation.NoTestsRemainException
-import org.junit.runner.manipulation.Sortable
-import org.junit.runner.manipulation.Sorter
-import org.junit.runner.notification.RunNotifier
-import kotlin.jvm.Throws
-
-/**
- * A runner that can skip tests based on the development SDK as defined in [DevSdkIgnoreRule].
- *
- * Generally [DevSdkIgnoreRule] should be used for that purpose (using rules is preferable over
- * replacing the test runner), however JUnit runners inspect all methods in the test class before
- * processing test rules. This may cause issues if the test methods are referencing classes that do
- * not exist on the SDK of the device the test is run on.
- *
- * This runner inspects [IgnoreAfter] and [IgnoreUpTo] annotations on the test class, and will skip
- * the whole class if they do not match the development SDK as defined in [DevSdkIgnoreRule].
- * Otherwise, it will delegate to [AndroidJUnit4] to run the test as usual.
- *
- * Example usage:
- *
- * @RunWith(DevSdkIgnoreRunner::class)
- * @IgnoreUpTo(Build.VERSION_CODES.Q)
- * class MyTestClass { ... }
- */
-class DevSdkIgnoreRunner(private val klass: Class<*>) : Runner(), Filterable, Sortable {
- private val baseRunner = klass.let {
- val ignoreAfter = it.getAnnotation(IgnoreAfter::class.java)
- val ignoreUpTo = it.getAnnotation(IgnoreUpTo::class.java)
-
- if (isDevSdkInRange(ignoreUpTo, ignoreAfter)) AndroidJUnit4(klass) else null
- }
-
- override fun run(notifier: RunNotifier) {
- if (baseRunner != null) {
- baseRunner.run(notifier)
- return
- }
-
- // Report a single, skipped placeholder test for this class, as the class is expected to
- // report results when run. In practice runners that apply the Filterable implementation
- // would see a NoTestsRemainException and not call the run method.
- notifier.fireTestIgnored(
- Description.createTestDescription(klass, "skippedClassForDevSdkMismatch"))
- }
-
- override fun getDescription(): Description {
- return baseRunner?.description ?: Description.createSuiteDescription(klass)
- }
-
- /**
- * Get the test count before applying the [Filterable] implementation.
- */
- override fun testCount(): Int {
- // When ignoring the tests, a skipped placeholder test is reported, so test count is 1.
- return baseRunner?.testCount() ?: 1
- }
-
- @Throws(NoTestsRemainException::class)
- override fun filter(filter: Filter?) {
- baseRunner?.filter(filter) ?: throw NoTestsRemainException()
- }
-
- override fun sort(sorter: Sorter?) {
- baseRunner?.sort(sorter)
- }
-} \ No newline at end of file
diff --git a/common/testutils/devicetests/com/android/testutils/DeviceConfigRule.kt b/common/testutils/devicetests/com/android/testutils/DeviceConfigRule.kt
deleted file mode 100644
index 3d98cc38..00000000
--- a/common/testutils/devicetests/com/android/testutils/DeviceConfigRule.kt
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2022 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.READ_DEVICE_CONFIG
-import android.Manifest.permission.WRITE_DEVICE_CONFIG
-import android.provider.DeviceConfig
-import android.util.Log
-import com.android.modules.utils.build.SdkLevel
-import com.android.testutils.FunctionalUtils.ThrowingRunnable
-import org.junit.rules.TestRule
-import org.junit.runner.Description
-import org.junit.runners.model.Statement
-import java.util.concurrent.CompletableFuture
-import java.util.concurrent.Executor
-import java.util.concurrent.TimeUnit
-
-private val TAG = DeviceConfigRule::class.simpleName
-
-private const val TIMEOUT_MS = 20_000L
-
-/**
- * A [TestRule] that helps set [DeviceConfig] for tests and clean up the test configuration
- * automatically on teardown.
- *
- * The rule can also optionally retry tests when they fail following an external change of
- * DeviceConfig before S; this typically happens because device config flags are synced while the
- * test is running, and DisableConfigSyncTargetPreparer is only usable starting from S.
- *
- * @param retryCountBeforeSIfConfigChanged if > 0, when the test fails before S, check if
- * the configs that were set through this rule were changed, and retry the test
- * up to the specified number of times if yes.
- */
-class DeviceConfigRule @JvmOverloads constructor(
- val retryCountBeforeSIfConfigChanged: Int = 0
-) : TestRule {
- // Maps (namespace, key) -> value
- private val originalConfig = mutableMapOf<Pair<String, String>, String?>()
- private val usedConfig = mutableMapOf<Pair<String, String>, String?>()
-
- /**
- * Actions to be run after cleanup of the config, for the current test only.
- */
- private val currentTestCleanupActions = mutableListOf<ThrowingRunnable>()
-
- override fun apply(base: Statement, description: Description): Statement {
- return TestValidationUrlStatement(base, description)
- }
-
- private inner class TestValidationUrlStatement(
- private val base: Statement,
- private val description: Description
- ) : Statement() {
- override fun evaluate() {
- var retryCount = if (SdkLevel.isAtLeastS()) 1 else retryCountBeforeSIfConfigChanged + 1
- while (retryCount > 0) {
- retryCount--
- tryTest {
- base.evaluate()
- // Can't use break/return out of a loop here because this is a tryTest lambda,
- // so set retryCount to exit instead
- retryCount = 0
- }.catch<Throwable> { e -> // junit AssertionFailedError does not extend Exception
- if (retryCount == 0) throw e
- usedConfig.forEach { (key, value) ->
- val currentValue = runAsShell(READ_DEVICE_CONFIG) {
- DeviceConfig.getProperty(key.first, key.second)
- }
- if (currentValue != value) {
- Log.w(TAG, "Test failed with unexpected device config change, retrying")
- return@catch
- }
- }
- throw e
- } cleanupStep {
- runAsShell(WRITE_DEVICE_CONFIG) {
- originalConfig.forEach { (key, value) ->
- DeviceConfig.setProperty(
- key.first, key.second, value, false /* makeDefault */)
- }
- }
- } cleanupStep {
- originalConfig.clear()
- usedConfig.clear()
- } cleanup {
- // Fold all cleanup actions into cleanup steps of an empty tryTest, so they are
- // all run even if exceptions are thrown, and exceptions are reported properly.
- currentTestCleanupActions.fold(tryTest { }) {
- tryBlock, action -> tryBlock.cleanupStep { action.run() }
- }.cleanup {
- currentTestCleanupActions.clear()
- }
- }
- }
- }
- }
-
- /**
- * Set a configuration key/value. After the test case ends, it will be restored to the value it
- * had when this method was first called.
- */
- fun setConfig(namespace: String, key: String, value: String?): String? {
- Log.i(TAG, "Setting config \"$key\" to \"$value\"")
- val readWritePermissions = arrayOf(READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG)
-
- val keyPair = Pair(namespace, key)
- val existingValue = runAsShell(*readWritePermissions) {
- DeviceConfig.getProperty(namespace, key)
- }
- if (!originalConfig.containsKey(keyPair)) {
- originalConfig[keyPair] = existingValue
- }
- usedConfig[keyPair] = value
- if (existingValue == value) {
- // Already the correct value. There may be a race if a change is already in flight,
- // but if multiple threads update the config there is no way to fix that anyway.
- Log.i(TAG, "\"$key\" already had value \"$value\"")
- return value
- }
-
- val future = CompletableFuture<String>()
- val listener = DeviceConfig.OnPropertiesChangedListener {
- // The listener receives updates for any change to any key, so don't react to
- // changes that do not affect the relevant key
- if (!it.keyset.contains(key)) return@OnPropertiesChangedListener
- // "null" means absent in DeviceConfig : there is no such thing as a present but
- // null value, so the following works even if |value| is null.
- if (it.getString(key, null) == value) {
- future.complete(value)
- }
- }
-
- return tryTest {
- runAsShell(*readWritePermissions) {
- DeviceConfig.addOnPropertiesChangedListener(
- DeviceConfig.NAMESPACE_CONNECTIVITY,
- inlineExecutor,
- listener)
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_CONNECTIVITY,
- key,
- value,
- false /* makeDefault */)
- // Don't drop the permission until the config is applied, just in case
- future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
- }.also {
- Log.i(TAG, "Config \"$key\" successfully set to \"$value\"")
- }
- } cleanup {
- DeviceConfig.removeOnPropertiesChangedListener(listener)
- }
- }
-
- private val inlineExecutor get() = Executor { r -> r.run() }
-
- /**
- * Add an action to be run after config cleanup when the current test case ends.
- */
- fun runAfterNextCleanup(action: ThrowingRunnable) {
- currentTestCleanupActions.add(action)
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/DeviceInfoUtils.java b/common/testutils/devicetests/com/android/testutils/DeviceInfoUtils.java
deleted file mode 100644
index ce55fdc1..00000000
--- a/common/testutils/devicetests/com/android/testutils/DeviceInfoUtils.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2022 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.os.VintfRuntimeInfo;
-import android.text.TextUtils;
-import android.util.Pair;
-
-import java.util.Objects;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Utilities for device information.
- */
-public class DeviceInfoUtils {
- /**
- * Class for a three-part kernel version number.
- */
- public static class KVersion {
- public final int major;
- public final int minor;
- public final int sub;
-
- public KVersion(int major, int minor, int sub) {
- this.major = major;
- this.minor = minor;
- this.sub = sub;
- }
-
- /**
- * Compares with other version numerically.
- *
- * @param other the other version to compare
- * @return the value 0 if this == other;
- * a value less than 0 if this < other and
- * a value greater than 0 if this > other.
- */
- public int compareTo(final KVersion other) {
- int res = Integer.compare(this.major, other.major);
- if (res == 0) {
- res = Integer.compare(this.minor, other.minor);
- }
- if (res == 0) {
- res = Integer.compare(this.sub, other.sub);
- }
- return res;
- }
-
- /**
- * At least satisfied with the given version.
- *
- * @param from the start version to compare
- * @return return true if this version is at least satisfied with the given version.
- * otherwise, return false.
- */
- public boolean isAtLeast(final KVersion from) {
- return compareTo(from) >= 0;
- }
-
- /**
- * Falls within the given range [from, to).
- *
- * @param from the start version to compare
- * @param to the end version to compare
- * @return return true if this version falls within the given range.
- * otherwise, return false.
- */
- public boolean isInRange(final KVersion from, final KVersion to) {
- return isAtLeast(from) && !isAtLeast(to);
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof KVersion)) return false;
- KVersion that = (KVersion) o;
- return this.major == that.major
- && this.minor == that.minor
- && this.sub == that.sub;
- }
- };
-
- /**
- * Get a two-part kernel version number (major and minor) from a given string.
- *
- * TODO: use class KVersion.
- */
- private static Pair<Integer, Integer> getMajorMinorVersion(String version) {
- // Only gets major and minor number of the version string.
- final Pattern versionPattern = Pattern.compile("^(\\d+)(\\.(\\d+))?.*");
- final Matcher m = versionPattern.matcher(version);
- if (m.matches()) {
- final int major = Integer.parseInt(m.group(1));
- final int minor = TextUtils.isEmpty(m.group(3)) ? 0 : Integer.parseInt(m.group(3));
- return new Pair<>(major, minor);
- } else {
- return new Pair<>(0, 0);
- }
- }
-
- /**
- * Compares two version strings numerically. Compare only major and minor number of the
- * version string. The version comparison uses #Integer.compare. Possible version
- * 5, 5.10, 5-beta1, 4.8-RC1, 4.7.10.10 and so on.
- *
- * @param s1 the first version string to compare
- * @param s2 the second version string to compare
- * @return the value 0 if s1 == s2;
- * a value less than 0 if s1 < s2 and
- * a value greater than 0 if s1 > s2.
- *
- * TODO: use class KVersion.
- */
- public static int compareMajorMinorVersion(final String s1, final String s2) {
- final Pair<Integer, Integer> v1 = getMajorMinorVersion(s1);
- final Pair<Integer, Integer> v2 = getMajorMinorVersion(s2);
-
- if (Objects.equals(v1.first, v2.first)) {
- return Integer.compare(v1.second, v2.second);
- } else {
- return Integer.compare(v1.first, v2.first);
- }
- }
-
- /**
- * Get a three-part kernel version number (major, minor and subminor) from a given string.
- * Any version string must at least have major and minor number. If the subminor number can't
- * be parsed from string. Assign zero as subminor number. Invalid version is treated as
- * version 0.0.0.
- */
- public static KVersion getMajorMinorSubminorVersion(final String version) {
- // The kernel version is a three-part version number (major, minor and subminor). Get
- // the three-part version numbers and discard the remaining stuff if any.
- // For example:
- // 4.19.220-g500ede0aed22-ab8272303 --> 4.19.220
- // 5.17-rc6-g52099515ca00-ab8032400 --> 5.17.0
- final Pattern versionPattern = Pattern.compile("^(\\d+)\\.(\\d+)(\\.(\\d+))?.*");
- final Matcher m = versionPattern.matcher(version);
- if (m.matches()) {
- final int major = Integer.parseInt(m.group(1));
- final int minor = Integer.parseInt(m.group(2));
- final int sub = TextUtils.isEmpty(m.group(4)) ? 0 : Integer.parseInt(m.group(4));
- return new KVersion(major, minor, sub);
- } else {
- return new KVersion(0, 0, 0);
- }
- }
-
- /**
- * Check if the current kernel version is at least satisfied with the given version.
- *
- * @param version the start version to compare
- * @return return true if the current version is at least satisfied with the given version.
- * otherwise, return false.
- */
- public static boolean isKernelVersionAtLeast(final String version) {
- final String kernelVersion = VintfRuntimeInfo.getKernelVersion();
- final KVersion current = DeviceInfoUtils.getMajorMinorSubminorVersion(kernelVersion);
- final KVersion from = DeviceInfoUtils.getMajorMinorSubminorVersion(version);
- return current.isAtLeast(from);
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/DnsAnswerProvider.kt b/common/testutils/devicetests/com/android/testutils/DnsAnswerProvider.kt
deleted file mode 100644
index 6a804bf9..00000000
--- a/common/testutils/devicetests/com/android/testutils/DnsAnswerProvider.kt
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2022 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.net.DnsResolver.CLASS_IN
-import com.android.net.module.util.DnsPacket
-import com.android.net.module.util.DnsPacket.ANSECTION
-import java.net.InetAddress
-import java.util.concurrent.ConcurrentHashMap
-
-const val DEFAULT_TTL_S = 5L
-
-/**
- * Helper class to store the mapping of DNS queries.
- *
- * DnsAnswerProvider is built atop a ConcurrentHashMap and as such it provides the same
- * guarantees as ConcurrentHashMap between writing and reading elements. Specifically :
- * - Setting an answer happens-before reading the same answer.
- * - Callers can read and write concurrently from DnsAnswerProvider and expect no
- * ConcurrentModificationException.
- * Freshness of the answers depends on ordering of the threads ; if callers need a
- * freshness guarantee, they need to provide the happens-before relationship from a
- * write that they want to observe to the read that they need to be observed.
- */
-class DnsAnswerProvider {
- private val mDnsKeyToRecords = ConcurrentHashMap<String, List<DnsPacket.DnsRecord>>()
-
- /**
- * Get answer for the specified hostname.
- *
- * @param query the target hostname.
- * @param type type of record, could be A or AAAA.
- *
- * @return list of [DnsPacket.DnsRecord] associated to the query. Empty if no record matches.
- */
- fun getAnswer(query: String, type: Int) = mDnsKeyToRecords[query]
- .orEmpty().filter { it.nsType == type }
-
- /** Set answer for the specified {@code query}.
- *
- * @param query the target hostname
- * @param addresses [List<InetAddress>] which could be used to generate multiple A or AAAA
- * RRs with the corresponding addresses.
- */
- fun setAnswer(query: String, hosts: List<InetAddress>) = mDnsKeyToRecords.put(query, hosts.map {
- DnsPacket.DnsRecord.makeAOrAAAARecord(ANSECTION, query, CLASS_IN, DEFAULT_TTL_S, it)
- })
-
- fun clearAnswer(query: String) = mDnsKeyToRecords.remove(query)
- fun clearAll() = mDnsKeyToRecords.clear()
-} \ No newline at end of file
diff --git a/common/testutils/devicetests/com/android/testutils/DumpTestUtils.java b/common/testutils/devicetests/com/android/testutils/DumpTestUtils.java
deleted file mode 100644
index d1037488..00000000
--- a/common/testutils/devicetests/com/android/testutils/DumpTestUtils.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2022 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 static com.android.testutils.TestPermissionUtil.runAsShell;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.system.ErrnoException;
-import android.system.Os;
-
-import libcore.io.IoUtils;
-import libcore.io.Streams;
-
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * Utilities for testing output of service dumps.
- */
-public class DumpTestUtils {
-
- private static String dumpService(String serviceName, boolean adoptPermission, String... args)
- throws RemoteException, InterruptedException, ErrnoException {
- final IBinder ib = ServiceManager.getService(serviceName);
- FileDescriptor[] pipe = Os.pipe();
-
- // Start a thread to read the dump output, or dump might block if it fills the pipe.
- final CountDownLatch latch = new CountDownLatch(1);
- AtomicReference<String> output = new AtomicReference<>();
- // Used to send exceptions back to the main thread to ensure that the test fails cleanly.
- AtomicReference<Exception> exception = new AtomicReference<>();
- new Thread(() -> {
- try {
- output.set(Streams.readFully(
- new InputStreamReader(new FileInputStream(pipe[0]),
- StandardCharsets.UTF_8)));
- latch.countDown();
- } catch (Exception e) {
- exception.set(e);
- latch.countDown();
- }
- }).start();
-
- final int timeoutMs = 5_000;
- final String what = "service '" + serviceName + "' with args: " + Arrays.toString(args);
- try {
- if (adoptPermission) {
- runAsShell(android.Manifest.permission.DUMP, () -> ib.dump(pipe[1], args));
- } else {
- ib.dump(pipe[1], args);
- }
- IoUtils.closeQuietly(pipe[1]);
- assertTrue("Dump of " + what + " timed out after " + timeoutMs + "ms",
- latch.await(timeoutMs, TimeUnit.MILLISECONDS));
- } finally {
- // Closing the fds will terminate the thread if it's blocked on read.
- IoUtils.closeQuietly(pipe[0]);
- if (pipe[1].valid()) IoUtils.closeQuietly(pipe[1]);
- }
- if (exception.get() != null) {
- fail("Exception dumping " + what + ": " + exception.get());
- }
- return output.get();
- }
-
- /**
- * Dumps the specified service and returns a string. Sends a dump IPC to the given service
- * with the specified args and a pipe, then reads from the pipe in a separate thread.
- * The current process must already have the DUMP permission.
- *
- * @param serviceName the service to dump.
- * @param args the arguments to pass to the dump function.
- * @return The dump text.
- * @throws RemoteException dumping the service failed.
- * @throws InterruptedException the dump timed out.
- * @throws ErrnoException opening or closing the pipe for the dump failed.
- */
- public static String dumpService(String serviceName, String... args)
- throws RemoteException, InterruptedException, ErrnoException {
- return dumpService(serviceName, false, args);
- }
-
- /**
- * Dumps the specified service and returns a string. Sends a dump IPC to the given service
- * with the specified args and a pipe, then reads from the pipe in a separate thread.
- * Adopts the {@code DUMP} permission via {@code adoptShellPermissionIdentity} and then releases
- * it. This method should not be used if the caller already has the shell permission identity.
- * TODO: when Q and R are no longer supported, use
- * {@link android.app.UiAutomation#getAdoptedShellPermissions} to automatically acquire the
- * shell permission if the caller does not already have it.
- *
- * @param serviceName the service to dump.
- * @param args the arguments to pass to the dump function.
- * @return The dump text.
- * @throws RemoteException dumping the service failed.
- * @throws InterruptedException the dump timed out.
- * @throws ErrnoException opening or closing the pipe for the dump failed.
- */
- public static String dumpServiceWithShellPermission(String serviceName, String... args)
- throws RemoteException, InterruptedException, ErrnoException {
- return dumpService(serviceName, true, args);
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/FakeDns.kt b/common/testutils/devicetests/com/android/testutils/FakeDns.kt
deleted file mode 100644
index 1f82a35d..00000000
--- a/common/testutils/devicetests/com/android/testutils/FakeDns.kt
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2019 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.net.DnsResolver
-import android.net.InetAddresses
-import android.os.Looper
-import android.os.Handler
-import com.android.internal.annotations.GuardedBy
-import java.net.InetAddress
-import java.util.concurrent.Executor
-import org.mockito.invocation.InvocationOnMock
-import org.mockito.Mockito.any
-import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.doAnswer
-
-const val TYPE_UNSPECIFIED = -1
-// TODO: Integrate with NetworkMonitorTest.
-class FakeDns(val mockResolver: DnsResolver) {
- class DnsEntry(val hostname: String, val type: Int, val addresses: List<InetAddress>) {
- fun match(host: String, type: Int) = hostname.equals(host) && type == type
- }
-
- @GuardedBy("answers")
- val answers = ArrayList<DnsEntry>()
-
- fun getAnswer(hostname: String, type: Int): DnsEntry? = synchronized(answers) {
- return answers.firstOrNull { it.match(hostname, type) }
- }
-
- fun setAnswer(hostname: String, answer: Array<String>, type: Int) = synchronized(answers) {
- val ans = DnsEntry(hostname, type, generateAnswer(answer))
- // Replace or remove the existing one.
- when (val index = answers.indexOfFirst { it.match(hostname, type) }) {
- -1 -> answers.add(ans)
- else -> answers[index] = ans
- }
- }
-
- private fun generateAnswer(answer: Array<String>) =
- answer.filterNotNull().map { InetAddresses.parseNumericAddress(it) }
-
- fun startMocking() {
- // Mock DnsResolver.query() w/o type
- doAnswer {
- mockAnswer(it, 1, -1, 3, 5)
- }.`when`(mockResolver).query(any() /* network */, any() /* domain */, anyInt() /* flags */,
- any() /* executor */, any() /* cancellationSignal */, any() /*callback*/)
- // Mock DnsResolver.query() w/ type
- doAnswer {
- mockAnswer(it, 1, 2, 4, 6)
- }.`when`(mockResolver).query(any() /* network */, any() /* domain */, anyInt() /* nsType */,
- anyInt() /* flags */, any() /* executor */, any() /* cancellationSignal */,
- any() /*callback*/)
- }
-
- private fun mockAnswer(
- it: InvocationOnMock,
- posHos: Int,
- posType: Int,
- posExecutor: Int,
- posCallback: Int
- ) {
- val hostname = it.arguments[posHos] as String
- val executor = it.arguments[posExecutor] as Executor
- val callback = it.arguments[posCallback] as DnsResolver.Callback<List<InetAddress>>
- var type = if (posType != -1) it.arguments[posType] as Int else TYPE_UNSPECIFIED
- val answer = getAnswer(hostname, type)
-
- if (answer != null && !answer.addresses.isNullOrEmpty()) {
- Handler(Looper.getMainLooper()).post({ executor.execute({
- callback.onAnswer(answer.addresses, 0); }) })
- }
- }
-
- /** Clears all entries. */
- fun clearAll() = synchronized(answers) {
- answers.clear()
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/HandlerUtils.kt b/common/testutils/devicetests/com/android/testutils/HandlerUtils.kt
deleted file mode 100644
index f00ca116..00000000
--- a/common/testutils/devicetests/com/android/testutils/HandlerUtils.kt
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2019 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("HandlerUtils")
-
-package com.android.testutils
-
-import android.os.ConditionVariable
-import android.os.Handler
-import android.os.HandlerThread
-import android.util.Log
-import com.android.testutils.FunctionalUtils.ThrowingRunnable
-import com.android.testutils.FunctionalUtils.ThrowingSupplier
-import java.lang.Exception
-import java.util.concurrent.Executor
-import kotlin.test.fail
-
-private const val TAG = "HandlerUtils"
-
-/**
- * Block until the specified Handler or HandlerThread becomes idle, or until timeoutMs has passed.
- */
-fun HandlerThread.waitForIdle(timeoutMs: Int) = threadHandler.waitForIdle(timeoutMs.toLong())
-fun HandlerThread.waitForIdle(timeoutMs: Long) = threadHandler.waitForIdle(timeoutMs)
-fun Handler.waitForIdle(timeoutMs: Int) = waitForIdle(timeoutMs.toLong())
-fun Handler.waitForIdle(timeoutMs: Long) {
- val cv = ConditionVariable(false)
- post(cv::open)
- if (!cv.block(timeoutMs)) {
- fail("Handler did not become idle after ${timeoutMs}ms")
- }
-}
-
-/**
- * Block until the given Serial Executor becomes idle, or until timeoutMs has passed.
- */
-fun waitForIdleSerialExecutor(executor: Executor, timeoutMs: Long) {
- val cv = ConditionVariable()
- executor.execute(cv::open)
- if (!cv.block(timeoutMs)) {
- fail("Executor did not become idle after ${timeoutMs}ms")
- }
-}
-
-/**
- * Executes a block of code that returns a value, making its side effects visible on the caller and
- * the handler thread.
- *
- * After this function returns, the side effects of the passed block of code are guaranteed to be
- * observed both on the thread running the handler and on the thread running this method.
- * To achieve this, this method runs the passed block on the handler and blocks this thread
- * until it's executed, so keep in mind this method will block, (including, if the handler isn't
- * running, blocking forever).
- */
-fun <T> visibleOnHandlerThread(handler: Handler, supplier: ThrowingSupplier<T>): T {
- val cv = ConditionVariable()
- var rv: Result<T> = Result.failure(RuntimeException("Not run"))
- handler.post {
- try {
- rv = Result.success(supplier.get())
- } catch (exception: Exception) {
- Log.e(TAG, "visibleOnHandlerThread caught exception", exception)
- rv = Result.failure(exception)
- }
- cv.open()
- }
- // After block() returns, the handler thread has seen the change (since it ran it)
- // and this thread also has seen the change (since cv.open() happens-before cv.block()
- // returns).
- cv.block()
- return rv.getOrThrow()
-}
-
-/** Overload of visibleOnHandlerThread but executes a block of code that does not return a value. */
-inline fun visibleOnHandlerThread(handler: Handler, r: ThrowingRunnable){
- visibleOnHandlerThread(handler, ThrowingSupplier<Unit> { r.run() })
-}
diff --git a/common/testutils/devicetests/com/android/testutils/NatExternalPacketForwarder.kt b/common/testutils/devicetests/com/android/testutils/NatExternalPacketForwarder.kt
deleted file mode 100644
index d7961a08..00000000
--- a/common/testutils/devicetests/com/android/testutils/NatExternalPacketForwarder.kt
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2023 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 java.io.FileDescriptor
-import java.net.InetAddress
-
-/**
- * A class that forwards packets from the external {@link TestNetworkInterface} to the internal
- * {@link TestNetworkInterface} with NAT. See {@link NatPacketForwarderBase} for detail.
- */
-class NatExternalPacketForwarder(
- srcFd: FileDescriptor,
- mtu: Int,
- dstFd: FileDescriptor,
- extAddr: InetAddress,
- natMap: PacketBridge.NatMap
-) : NatPacketForwarderBase(srcFd, mtu, dstFd, extAddr, natMap) {
-
- /**
- * Rewrite addresses, ports and fix up checksums for packets received on the external
- * interface.
- *
- * Incoming response from external interface which is being forwarded to the internal
- * interface with translated address, e.g. 1.2.3.4:80 -> 8.8.8.8:1234
- * will be translated into 8.8.8.8:80 -> 192.168.1.1:5678.
- *
- * For packets that are not an incoming response, do not forward them to the
- * internal interface.
- */
- override fun preparePacketForForwarding(buf: ByteArray, len: Int, version: Int, proto: Int) {
- val (addrPos, addrLen) = getAddressPositionAndLength(version)
-
- // TODO: support one external address per ip version.
- val extAddrBuf = mExtAddr.address
- if (addrLen != extAddrBuf.size) throw IllegalStateException("Packet IP version mismatch")
-
- // Get internal address by port.
- val transportOffset =
- if (version == 4) PacketReflector.IPV4_HEADER_LENGTH
- else PacketReflector.IPV6_HEADER_LENGTH
- val dstPort = getPortAt(buf, transportOffset + DESTINATION_PORT_OFFSET)
- val intAddrInfo = synchronized(mNatMap) { mNatMap.fromExternalPort(dstPort) }
- // No mapping, skip. This usually happens if the connection is initiated directly on
- // the external interface, e.g. DNS64 resolution, network validation, etc.
- if (intAddrInfo == null) return
-
- val intAddrBuf = intAddrInfo.address.address
- val intPort = intAddrInfo.port
-
- // Copy the original destination to into the source address.
- for (i in 0 until addrLen) {
- buf[addrPos + i] = buf[addrPos + addrLen + i]
- }
-
- // Copy the internal address into the destination address.
- for (i in 0 until addrLen) {
- buf[addrPos + addrLen + i] = intAddrBuf[i]
- }
-
- // Copy the internal port into the destination port.
- setPortAt(intPort, buf, transportOffset + DESTINATION_PORT_OFFSET)
-
- // Fix IP and Transport layer checksum.
- fixPacketChecksum(buf, len, version, proto.toByte())
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/NatInternalPacketForwarder.kt b/common/testutils/devicetests/com/android/testutils/NatInternalPacketForwarder.kt
deleted file mode 100644
index fa39d19e..00000000
--- a/common/testutils/devicetests/com/android/testutils/NatInternalPacketForwarder.kt
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2023 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 java.io.FileDescriptor
-import java.net.InetAddress
-
-/**
- * A class that forwards packets from the internal {@link TestNetworkInterface} to the external
- * {@link TestNetworkInterface} with NAT. See {@link NatPacketForwarderBase} for detail.
- */
-class NatInternalPacketForwarder(
- srcFd: FileDescriptor,
- mtu: Int,
- dstFd: FileDescriptor,
- extAddr: InetAddress,
- natMap: PacketBridge.NatMap
-) : NatPacketForwarderBase(srcFd, mtu, dstFd, extAddr, natMap) {
-
- /**
- * Rewrite addresses, ports and fix up checksums for packets received on the internal
- * interface.
- *
- * Outgoing packet from the internal interface which is being forwarded to the
- * external interface with translated address, e.g. 192.168.1.1:5678 -> 8.8.8.8:80
- * will be translated into 8.8.8.8:1234 -> 1.2.3.4:80.
- *
- * The external port, e.g. 1234 in the above example, is the port number assigned by
- * the forwarder when creating the mapping to identify the source address and port when
- * the response is coming from the external interface. See {@link PacketBridge.NatMap}
- * for detail.
- */
- override fun preparePacketForForwarding(buf: ByteArray, len: Int, version: Int, proto: Int) {
- val (addrPos, addrLen) = getAddressPositionAndLength(version)
-
- // TODO: support one external address per ip version.
- val extAddrBuf = mExtAddr.address
- if (addrLen != extAddrBuf.size) throw IllegalStateException("Packet IP version mismatch")
-
- val srcAddr = getInetAddressAt(buf, addrPos, addrLen)
-
- // Copy the original destination to into the source address.
- for (i in 0 until addrLen) {
- buf[addrPos + i] = buf[addrPos + addrLen + i]
- }
-
- // Copy the external address into the destination address.
- for (i in 0 until addrLen) {
- buf[addrPos + addrLen + i] = extAddrBuf[i]
- }
-
- // Add an entry to NAT mapping table.
- val transportOffset =
- if (version == 4) PacketReflector.IPV4_HEADER_LENGTH
- else PacketReflector.IPV6_HEADER_LENGTH
- val srcPort = getPortAt(buf, transportOffset)
- val extPort = synchronized(mNatMap) { mNatMap.toExternalPort(srcAddr, srcPort, proto) }
- // Copy the external port to into the source port.
- setPortAt(extPort, buf, transportOffset)
-
- // Fix IP and Transport layer checksum.
- fixPacketChecksum(buf, len, version, proto.toByte())
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/NatPacketForwarderBase.java b/common/testutils/devicetests/com/android/testutils/NatPacketForwarderBase.java
deleted file mode 100644
index 0a2b5d41..00000000
--- a/common/testutils/devicetests/com/android/testutils/NatPacketForwarderBase.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright (C) 2023 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 static com.android.testutils.PacketReflector.IPPROTO_TCP;
-import static com.android.testutils.PacketReflector.IPPROTO_UDP;
-import static com.android.testutils.PacketReflector.IPV4_HEADER_LENGTH;
-import static com.android.testutils.PacketReflector.IPV6_HEADER_LENGTH;
-import static com.android.testutils.PacketReflector.IPV6_PROTO_OFFSET;
-import static com.android.testutils.PacketReflector.TCP_HEADER_LENGTH;
-import static com.android.testutils.PacketReflector.UDP_HEADER_LENGTH;
-
-import android.annotation.NonNull;
-import android.net.TestNetworkInterface;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.util.Log;
-
-import androidx.annotation.GuardedBy;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.util.Objects;
-
-/**
- * A class that forwards packets from a {@link TestNetworkInterface} to another
- * {@link TestNetworkInterface} with NAT.
- *
- * For testing purposes, a {@link TestNetworkInterface} provides a {@link FileDescriptor}
- * which allows content injection on the test network. However, this could be hard to use
- * because the callers need to compose IP packets in order to inject content to the
- * test network.
- *
- * In order to remove the need of composing the IP packets, this class forwards IP packets to
- * the {@link FileDescriptor} of another {@link TestNetworkInterface} instance. Thus,
- * the TCP/IP headers could be parsed/composed automatically by the protocol stack of this
- * additional {@link TestNetworkInterface}, while the payload is supplied by the
- * servers run on the interface.
- *
- * To make it work, an internal interface and an external interface are defined, where
- * the client might send packets from the internal interface which are originated from
- * multiple addresses to a server that listens on the external address.
- *
- * When forwarding the outgoing packet on the internal interface, a simple NAT mechanism
- * is implemented during forwarding, which will swap the source and destination,
- * but replacing the source address with the external address,
- * e.g. 192.168.1.1:1234 -> 8.8.8.8:80 will be translated into 8.8.8.8:1234 -> 1.2.3.4:80.
- *
- * For the above example, a client who sends http request will have a hallucination that
- * it is talking to a remote server at 8.8.8.8. Also, the server listens on 1.2.3.4 will
- * have a different hallucination that the request is sent from a remote client at 8.8.8.8,
- * to a local address 1.2.3.4.
- *
- * And a NAT mapping is created at the time when the outgoing packet is forwarded.
- * With a different internal source port, the instance learned that when a response with the
- * destination port 1234, it should forward the packet to the internal address 192.168.1.1.
- *
- * For the incoming packet received from external interface, for example a http response sent
- * from the http server, the same mechanism is applied but in a different direction,
- * where the source and destination will be swapped, and the source address will be replaced
- * with the internal address, which is obtained from the NAT mapping described above.
- */
-public abstract class NatPacketForwarderBase extends Thread {
- private static final String TAG = "NatPacketForwarder";
- static final int DESTINATION_PORT_OFFSET = 2;
-
- // The source fd to read packets from.
- @NonNull
- final FileDescriptor mSrcFd;
- // The buffer to temporarily hold the entire packet after receiving.
- @NonNull
- final byte[] mBuf;
- // The destination fd to write packets to.
- @NonNull
- final FileDescriptor mDstFd;
- // The NAT mapping table shared between two NatPacketForwarder instances to map from
- // the source port to the associated internal address. The map can be read/write from two
- // different threads on any given time whenever receiving packets on the
- // {@link TestNetworkInterface}. Thus, synchronize on the object when reading/writing is needed.
- @GuardedBy("mNatMap")
- @NonNull
- final PacketBridge.NatMap mNatMap;
- // The address of the external interface. See {@link NatPacketForwarder}.
- @NonNull
- final InetAddress mExtAddr;
-
- /**
- * Construct a {@link NatPacketForwarderBase}.
- *
- * This class reads packets from {@code srcFd} of a {@link TestNetworkInterface}, and
- * forwards them to the {@code dstFd} of another {@link TestNetworkInterface} with
- * NAT applied. See {@link NatPacketForwarderBase}.
- *
- * To apply NAT, the address of the external interface needs to be supplied through
- * {@code extAddr} to identify the external interface. And a shared NAT mapping table,
- * {@code natMap} is needed to be shared between these two instances.
- *
- * Note that this class is not useful if the instance is not managed by a
- * {@link PacketBridge} to set up a two-way communication.
- *
- * @param srcFd {@link FileDescriptor} to read packets from.
- * @param mtu MTU of the test network.
- * @param dstFd {@link FileDescriptor} to write packets to.
- * @param extAddr the external address, which is the address of the external interface.
- * See {@link NatPacketForwarderBase}.
- * @param natMap the NAT mapping table shared between two {@link NatPacketForwarderBase}
- * instance.
- */
- public NatPacketForwarderBase(@NonNull FileDescriptor srcFd, int mtu,
- @NonNull FileDescriptor dstFd, @NonNull InetAddress extAddr,
- @NonNull PacketBridge.NatMap natMap) {
- super(TAG);
- mSrcFd = Objects.requireNonNull(srcFd);
- mBuf = new byte[mtu];
- mDstFd = Objects.requireNonNull(dstFd);
- mExtAddr = Objects.requireNonNull(extAddr);
- mNatMap = Objects.requireNonNull(natMap);
- }
-
- /**
- * A method to prepare forwarding packets between two instances of {@link TestNetworkInterface},
- * which includes re-write addresses, ports and fix up checksums.
- * Subclasses should override this method to implement a simple NAT.
- */
- abstract void preparePacketForForwarding(@NonNull byte[] buf, int len, int version, int proto);
-
- private void forwardPacket(@NonNull byte[] buf, int len) {
- try {
- Os.write(mDstFd, buf, 0, len);
- } catch (ErrnoException | IOException e) {
- Log.e(TAG, "Error writing packet: " + e.getMessage());
- }
- }
-
- // Reads one packet from mSrcFd, and writes the packet to the mDstFd for supported protocols.
- private void processPacket() {
- final int len = PacketReflectorUtil.readPacket(mSrcFd, mBuf);
- if (len < 1) {
- // Usually happens when socket read is being interrupted, e.g. stopping PacketForwarder.
- return;
- }
-
- final int version = mBuf[0] >>> 4;
- final int protoPos, ipHdrLen;
- switch (version) {
- case 4:
- ipHdrLen = IPV4_HEADER_LENGTH;
- protoPos = PacketReflector.IPV4_PROTO_OFFSET;
- break;
- case 6:
- ipHdrLen = IPV6_HEADER_LENGTH;
- protoPos = IPV6_PROTO_OFFSET;
- break;
- default:
- throw new IllegalStateException("Unexpected version: " + version);
- }
- if (len < ipHdrLen) {
- throw new IllegalStateException("Unexpected buffer length: " + len);
- }
-
- final byte proto = mBuf[protoPos];
- final int transportHdrLen;
- switch (proto) {
- case IPPROTO_TCP:
- transportHdrLen = TCP_HEADER_LENGTH;
- break;
- case IPPROTO_UDP:
- transportHdrLen = UDP_HEADER_LENGTH;
- break;
- // TODO: Support ICMP.
- default:
- return; // Unknown protocol, ignored.
- }
-
- if (len < ipHdrLen + transportHdrLen) {
- throw new IllegalStateException("Unexpected buffer length: " + len);
- }
- // Re-write addresses, ports and fix up checksums.
- preparePacketForForwarding(mBuf, len, version, proto);
- // Send the packet to the destination fd.
- forwardPacket(mBuf, len);
- }
-
- @Override
- public void run() {
- Log.i(TAG, "starting fd=" + mSrcFd + " valid=" + mSrcFd.valid());
- while (!interrupted() && mSrcFd.valid()) {
- processPacket();
- }
- Log.i(TAG, "exiting fd=" + mSrcFd + " valid=" + mSrcFd.valid());
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/NetlinkTestUtils.kt b/common/testutils/devicetests/com/android/testutils/NetlinkTestUtils.kt
deleted file mode 100644
index 3f5460bf..00000000
--- a/common/testutils/devicetests/com/android/testutils/NetlinkTestUtils.kt
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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("NetlinkTestUtils")
-
-package com.android.testutils
-
-import com.android.net.module.util.netlink.NetlinkConstants.RTM_DELNEIGH
-import com.android.net.module.util.netlink.NetlinkConstants.RTM_NEWNEIGH
-import libcore.util.HexEncoding
-import libcore.util.HexEncoding.encodeToString
-import java.net.Inet6Address
-import java.net.InetAddress
-
-private const val VRRP_MAC_ADDR = "00005e000164"
-
-/**
- * Make a RTM_NEWNEIGH netlink message.
- */
-@JvmOverloads
-fun makeNewNeighMessage(
- neighAddr: InetAddress,
- nudState: Short,
- linkLayerAddr: String = VRRP_MAC_ADDR
-) = makeNeighborMessage(
- neighAddr = neighAddr,
- type = RTM_NEWNEIGH,
- nudState = nudState,
- linkLayerAddr = linkLayerAddr
-)
-
-/**
- * Make a RTM_DELNEIGH netlink message.
- */
-fun makeDelNeighMessage(
- neighAddr: InetAddress,
- nudState: Short
-) = makeNeighborMessage(
- neighAddr = neighAddr,
- type = RTM_DELNEIGH,
- nudState = nudState
-)
-
-private fun makeNeighborMessage(
- neighAddr: InetAddress,
- type: Short,
- nudState: Short,
- linkLayerAddr: String = VRRP_MAC_ADDR
-) = HexEncoding.decode(
- /* ktlint-disable indent */
- // -- struct nlmsghdr --
- // length = 88 or 76:
- (if (neighAddr is Inet6Address) "58000000" else "4c000000") +
- type.toLEHex() + // type
- "0000" + // flags
- "00000000" + // seqno
- "00000000" + // pid (0 == kernel)
- // struct ndmsg
- // family (AF_INET6 or AF_INET)
- (if (neighAddr is Inet6Address) "0a" else "02") +
- "00" + // pad1
- "0000" + // pad2
- "15000000" + // interface index (21 == wlan0, on test device)
- nudState.toLEHex() + // NUD state
- "00" + // flags
- "01" + // type
- // -- struct nlattr: NDA_DST --
- // length = 20 or 8:
- (if (neighAddr is Inet6Address) "1400" else "0800") +
- "0100" + // type (1 == NDA_DST, for neighbor messages)
- // IP address:
- encodeToString(neighAddr.address) +
- // -- struct nlattr: NDA_LLADDR --
- "0a00" + // length = 10
- "0200" + // type (2 == NDA_LLADDR, for neighbor messages)
- linkLayerAddr + // MAC Address(default == 00:00:5e:00:01:64)
- "0000" + // padding, for 4 byte alignment
- // -- struct nlattr: NDA_PROBES --
- "0800" + // length = 8
- "0400" + // type (4 == NDA_PROBES, for neighbor messages)
- "01000000" + // number of probes
- // -- struct nlattr: NDA_CACHEINFO --
- "1400" + // length = 20
- "0300" + // type (3 == NDA_CACHEINFO, for neighbor messages)
- "05190000" + // ndm_used, as "clock ticks ago"
- "05190000" + // ndm_confirmed, as "clock ticks ago"
- "190d0000" + // ndm_updated, as "clock ticks ago"
- "00000000", // ndm_refcnt
- false /* allowSingleChar */)
- /* ktlint-enable indent */
-
-/**
- * Convert a [Short] to a little-endian hex string.
- */
-private fun Short.toLEHex() = String.format("%04x", java.lang.Short.reverseBytes(this))
diff --git a/common/testutils/devicetests/com/android/testutils/NetworkStatsProviderCbStubCompat.java b/common/testutils/devicetests/com/android/testutils/NetworkStatsProviderCbStubCompat.java
deleted file mode 100644
index 642da7ac..00000000
--- a/common/testutils/devicetests/com/android/testutils/NetworkStatsProviderCbStubCompat.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2021 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.net.NetworkStats;
-import android.net.netstats.provider.INetworkStatsProviderCallback;
-import android.os.RemoteException;
-
-/**
- * A shim class that allows {@link TestableNetworkStatsProviderCbBinder} to be built against
- * different SDK versions.
- */
-public class NetworkStatsProviderCbStubCompat extends INetworkStatsProviderCallback.Stub {
- @Override
- public void notifyStatsUpdated(int token, NetworkStats ifaceStats, NetworkStats uidStats)
- throws RemoteException {}
-
- @Override
- public void notifyAlertReached() throws RemoteException {}
-
- /** Added in T. */
- public void notifyLimitReached() throws RemoteException {}
-
- /** Added in T. */
- public void notifyWarningReached() throws RemoteException {}
-
- /** Added in S, removed in T. */
- public void notifyWarningOrLimitReached() throws RemoteException {}
-
- @Override
- public void unregister() throws RemoteException {}
-}
diff --git a/common/testutils/devicetests/com/android/testutils/NetworkStatsProviderStubCompat.java b/common/testutils/devicetests/com/android/testutils/NetworkStatsProviderStubCompat.java
deleted file mode 100644
index a77aa025..00000000
--- a/common/testutils/devicetests/com/android/testutils/NetworkStatsProviderStubCompat.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2021 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.net.netstats.provider.INetworkStatsProvider;
-
-/**
- * A shim class that allows {@link TestableNetworkStatsProviderBinder} to be built against
- * different SDK versions.
- */
-public class NetworkStatsProviderStubCompat extends INetworkStatsProvider.Stub {
- @Override
- public void onRequestStatsUpdate(int token) {}
-
- // Removed and won't be called in S+.
- public void onSetLimit(String iface, long quotaBytes) {}
-
- @Override
- public void onSetAlert(long bytes) {}
-
- // Added in S.
- public void onSetWarningAndLimit(String iface, long warningBytes, long limitBytes) {}
-}
diff --git a/common/testutils/devicetests/com/android/testutils/NetworkStatsUtils.kt b/common/testutils/devicetests/com/android/testutils/NetworkStatsUtils.kt
deleted file mode 100644
index 8324b25e..00000000
--- a/common/testutils/devicetests/com/android/testutils/NetworkStatsUtils.kt
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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.net.NetworkStats
-import kotlin.test.assertTrue
-
-@JvmOverloads
-fun orderInsensitiveEquals(
- leftStats: NetworkStats,
- rightStats: NetworkStats,
- compareTime: Boolean = false
-): Boolean {
- if (leftStats == rightStats) return true
- if (compareTime && leftStats.getElapsedRealtime() != rightStats.getElapsedRealtime()) {
- return false
- }
-
- // While operations such as add/subtract will preserve empty entries. This will make
- // the result be hard to verify during test. Remove them before comparing since they
- // are not really affect correctness.
- // TODO (b/152827872): Remove empty entries after addition/subtraction.
- val leftTrimmedEmpty = leftStats.removeEmptyEntries()
- val rightTrimmedEmpty = rightStats.removeEmptyEntries()
-
- if (leftTrimmedEmpty.size() != rightTrimmedEmpty.size()) return false
- val left = NetworkStats.Entry()
- val right = NetworkStats.Entry()
- // Order insensitive compare.
- for (i in 0 until leftTrimmedEmpty.size()) {
- leftTrimmedEmpty.getValues(i, left)
- val j: Int = rightTrimmedEmpty.findIndexHinted(left.iface, left.uid, left.set, left.tag,
- left.metered, left.roaming, left.defaultNetwork, i)
- if (j == -1) return false
- rightTrimmedEmpty.getValues(j, right)
- if (left != right) return false
- }
- return true
-}
-
-/**
- * Assert that two {@link NetworkStats} are equals, assuming the order of the records are not
- * necessarily the same.
- *
- * @note {@code elapsedRealtime} is not compared by default, given that in test cases that is not
- * usually used.
- */
-@JvmOverloads
-fun assertNetworkStatsEquals(
- expected: NetworkStats,
- actual: NetworkStats,
- compareTime: Boolean = false
-) {
- assertTrue(orderInsensitiveEquals(expected, actual, compareTime),
- "expected: " + expected + " but was: " + actual)
-}
-
-/**
- * Assert that after being parceled then unparceled, {@link NetworkStats} is equal to the original
- * object.
- */
-fun assertParcelingIsLossless(stats: NetworkStats) {
- assertParcelingIsLossless(stats, { a, b -> orderInsensitiveEquals(a, b) })
-}
diff --git a/common/testutils/devicetests/com/android/testutils/NonNullTestUtils.java b/common/testutils/devicetests/com/android/testutils/NonNullTestUtils.java
deleted file mode 100644
index 463c4706..00000000
--- a/common/testutils/devicetests/com/android/testutils/NonNullTestUtils.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2023 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.annotation.NonNull;
-
-/**
- * Utilities to help Kotlin test to verify java @NonNull code
- */
-public class NonNullTestUtils {
- /**
- * This method allows Kotlin to pass nullable to @NonNull java code for testing.
- * For Foo(@NonNull arg) java method, Kotlin can pass nullable variable by
- * Foo(NonNullTestUtils.nullUnsafe(nullableVar)).
- */
- @NonNull public static <T> T nullUnsafe(T v) {
- return v;
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/PacketBridge.kt b/common/testutils/devicetests/com/android/testutils/PacketBridge.kt
deleted file mode 100644
index d50f78a1..00000000
--- a/common/testutils/devicetests/com/android/testutils/PacketBridge.kt
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2023 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.content.Context
-import android.net.ConnectivityManager
-import android.net.LinkAddress
-import android.net.LinkProperties
-import android.net.Network
-import android.net.NetworkCapabilities
-import android.net.NetworkRequest
-import android.net.TestNetworkInterface
-import android.net.TestNetworkManager
-import android.net.TestNetworkSpecifier
-import android.os.Binder
-import com.android.testutils.RecorderCallback.CallbackEntry.Available
-import java.net.InetAddress
-import libcore.io.IoUtils
-
-private const val MIN_PORT_NUMBER = 1025
-private const val MAX_PORT_NUMBER = 65535
-
-/**
- * A class that set up two {@link TestNetworkInterface} with NAT, and forward packets between them.
- *
- * See {@link NatPacketForwarder} for more detailed information.
- */
-class PacketBridge(
- context: Context,
- internalAddr: LinkAddress,
- externalAddr: LinkAddress,
- dnsAddr: InetAddress
-) {
- private val natMap = NatMap()
- private val binder = Binder()
-
- private val cm = context.getSystemService(ConnectivityManager::class.java)!!
- private val tnm = context.getSystemService(TestNetworkManager::class.java)!!
-
- // Create test networks.
- private val internalIface = tnm.createTunInterface(listOf(internalAddr))
- private val externalIface = tnm.createTunInterface(listOf(externalAddr))
-
- // Register test networks to ConnectivityService.
- private val internalNetworkCallback: TestableNetworkCallback
- private val externalNetworkCallback: TestableNetworkCallback
- val internalNetwork: Network
- val externalNetwork: Network
- init {
- val (inCb, inNet) = createTestNetwork(internalIface, internalAddr, dnsAddr)
- val (exCb, exNet) = createTestNetwork(externalIface, externalAddr, dnsAddr)
- internalNetworkCallback = inCb
- externalNetworkCallback = exCb
- internalNetwork = inNet
- externalNetwork = exNet
- }
-
- // Setup the packet bridge.
- private val internalFd = internalIface.fileDescriptor.fileDescriptor
- private val externalFd = externalIface.fileDescriptor.fileDescriptor
-
- private val pr1 = NatInternalPacketForwarder(
- internalFd,
- 1500,
- externalFd,
- externalAddr.address,
- natMap
- )
- private val pr2 = NatExternalPacketForwarder(
- externalFd,
- 1500,
- internalFd,
- externalAddr.address,
- natMap
- )
-
- fun start() {
- IoUtils.setBlocking(internalFd, true /* blocking */)
- IoUtils.setBlocking(externalFd, true /* blocking */)
- pr1.start()
- pr2.start()
- }
-
- fun stop() {
- pr1.interrupt()
- pr2.interrupt()
- cm.unregisterNetworkCallback(internalNetworkCallback)
- cm.unregisterNetworkCallback(externalNetworkCallback)
- }
-
- /**
- * Creates a test network with given test TUN interface and addresses.
- */
- private fun createTestNetwork(
- testIface: TestNetworkInterface,
- addr: LinkAddress,
- dnsAddr: InetAddress
- ): Pair<TestableNetworkCallback, Network> {
- // Make a network request to hold the test network
- val nr = NetworkRequest.Builder()
- .clearCapabilities()
- .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
- .setNetworkSpecifier(TestNetworkSpecifier(testIface.interfaceName))
- .build()
- val testCb = TestableNetworkCallback()
- cm.requestNetwork(nr, testCb)
-
- val lp = LinkProperties().apply {
- addLinkAddress(addr)
- interfaceName = testIface.interfaceName
- addDnsServer(dnsAddr)
- }
- tnm.setupTestNetwork(lp, true /* isMetered */, binder)
-
- // Wait for available before return.
- val network = testCb.expect<Available>().network
- return testCb to network
- }
-
- /**
- * A helper class to maintain the mappings between internal addresses/ports and external
- * ports.
- *
- * This class assigns an unused external port number if the mapping between
- * srcaddress:srcport:protocol and the external port does not exist yet.
- *
- * Note that this class is not thread-safe. The instance of the class needs to be
- * synchronized in the callers when being used in multiple threads.
- */
- class NatMap {
- data class AddressInfo(val address: InetAddress, val port: Int, val protocol: Int)
-
- private val mToExternalPort = HashMap<AddressInfo, Int>()
- private val mFromExternalPort = HashMap<Int, AddressInfo>()
-
- // Skip well-known port 0~1024.
- private var nextExternalPort = MIN_PORT_NUMBER
-
- fun toExternalPort(addr: InetAddress, port: Int, protocol: Int): Int {
- val info = AddressInfo(addr, port, protocol)
- val extPort: Int
- if (!mToExternalPort.containsKey(info)) {
- extPort = nextExternalPort++
- if (nextExternalPort > MAX_PORT_NUMBER) {
- throw IllegalStateException("Available ports are exhausted")
- }
- mToExternalPort[info] = extPort
- mFromExternalPort[extPort] = info
- } else {
- extPort = mToExternalPort[info]!!
- }
- return extPort
- }
-
- fun fromExternalPort(port: Int): AddressInfo? {
- return mFromExternalPort[port]
- }
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/PacketReflector.java b/common/testutils/devicetests/com/android/testutils/PacketReflector.java
deleted file mode 100644
index 69392d44..00000000
--- a/common/testutils/devicetests/com/android/testutils/PacketReflector.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright (C) 2014 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 static android.system.OsConstants.ICMP6_ECHO_REPLY;
-import static android.system.OsConstants.ICMP6_ECHO_REQUEST;
-
-import android.annotation.NonNull;
-import android.net.TestNetworkInterface;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.util.Log;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.util.Objects;
-
-/**
- * A class that echoes packets received on a {@link TestNetworkInterface} back to itself.
- *
- * For testing purposes, sometimes a mocked environment to simulate a simple echo from the
- * server side is needed. This is particularly useful if the test, e.g. VpnTest, is
- * heavily relying on the outside world.
- *
- * This class reads packets from the {@link FileDescriptor} of a {@link TestNetworkInterface}, and:
- * 1. For TCP and UDP packets, simply swaps the source address and the destination
- * address, then send it back to the {@link FileDescriptor}.
- * 2. For ICMP ping packets, composes a ping reply and sends it back to the sender.
- * 3. Ignore all other packets.
- */
-public class PacketReflector extends Thread {
-
- static final int IPV4_HEADER_LENGTH = 20;
- static final int IPV6_HEADER_LENGTH = 40;
-
- static final int IPV4_ADDR_OFFSET = 12;
- static final int IPV6_ADDR_OFFSET = 8;
- static final int IPV4_ADDR_LENGTH = 4;
- static final int IPV6_ADDR_LENGTH = 16;
-
- static final int IPV4_PROTO_OFFSET = 9;
- static final int IPV6_PROTO_OFFSET = 6;
-
- static final byte IPPROTO_ICMP = 1;
- static final byte IPPROTO_TCP = 6;
- static final byte IPPROTO_UDP = 17;
- private static final byte IPPROTO_ICMPV6 = 58;
-
- private static final int ICMP_HEADER_LENGTH = 8;
- static final int TCP_HEADER_LENGTH = 20;
- static final int UDP_HEADER_LENGTH = 8;
-
- private static final byte ICMP_ECHO = 8;
- private static final byte ICMP_ECHOREPLY = 0;
-
- private static String TAG = "PacketReflector";
-
- @NonNull
- private final FileDescriptor mFd;
- @NonNull
- private final byte[] mBuf;
-
- /**
- * Construct a {@link PacketReflector} from the given {@code fd} of
- * a {@link TestNetworkInterface}.
- *
- * @param fd {@link FileDescriptor} to read/write packets.
- * @param mtu MTU of the test network.
- */
- public PacketReflector(@NonNull FileDescriptor fd, int mtu) {
- super("PacketReflector");
- mFd = Objects.requireNonNull(fd);
- mBuf = new byte[mtu];
- }
-
- private static void swapBytes(@NonNull byte[] buf, int pos1, int pos2, int len) {
- for (int i = 0; i < len; i++) {
- byte b = buf[pos1 + i];
- buf[pos1 + i] = buf[pos2 + i];
- buf[pos2 + i] = b;
- }
- }
-
- private static void swapAddresses(@NonNull byte[] buf, int version) {
- int addrPos, addrLen;
- switch (version) {
- case 4:
- addrPos = IPV4_ADDR_OFFSET;
- addrLen = IPV4_ADDR_LENGTH;
- break;
- case 6:
- addrPos = IPV6_ADDR_OFFSET;
- addrLen = IPV6_ADDR_LENGTH;
- break;
- default:
- throw new IllegalArgumentException();
- }
- swapBytes(buf, addrPos, addrPos + addrLen, addrLen);
- }
-
- // Reflect TCP packets: swap the source and destination addresses, but don't change the ports.
- // This is used by the test to "connect to itself" through the VPN.
- private void processTcpPacket(@NonNull byte[] buf, int version, int len, int hdrLen) {
- if (len < hdrLen + TCP_HEADER_LENGTH) {
- return;
- }
-
- // Swap src and dst IP addresses.
- swapAddresses(buf, version);
-
- // Send the packet back.
- writePacket(buf, len);
- }
-
- // Echo UDP packets: swap source and destination addresses, and source and destination ports.
- // This is used by the test to check that the bytes it sends are echoed back.
- private void processUdpPacket(@NonNull byte[] buf, int version, int len, int hdrLen) {
- if (len < hdrLen + UDP_HEADER_LENGTH) {
- return;
- }
-
- // Swap src and dst IP addresses.
- swapAddresses(buf, version);
-
- // Swap dst and src ports.
- int portOffset = hdrLen;
- swapBytes(buf, portOffset, portOffset + 2, 2);
-
- // Send the packet back.
- writePacket(buf, len);
- }
-
- private void processIcmpPacket(@NonNull byte[] buf, int version, int len, int hdrLen) {
- if (len < hdrLen + ICMP_HEADER_LENGTH) {
- return;
- }
-
- byte type = buf[hdrLen];
- if (!(version == 4 && type == ICMP_ECHO) &&
- !(version == 6 && type == (byte) ICMP6_ECHO_REQUEST)) {
- return;
- }
-
- // Save the ping packet we received.
- byte[] request = buf.clone();
-
- // Swap src and dst IP addresses, and send the packet back.
- // This effectively pings the device to see if it replies.
- swapAddresses(buf, version);
- writePacket(buf, len);
-
- // The device should have replied, and buf should now contain a ping response.
- int received = PacketReflectorUtil.readPacket(mFd, buf);
- if (received != len) {
- Log.i(TAG, "Reflecting ping did not result in ping response: " +
- "read=" + received + " expected=" + len);
- return;
- }
-
- byte replyType = buf[hdrLen];
- if ((type == ICMP_ECHO && replyType != ICMP_ECHOREPLY)
- || (type == (byte) ICMP6_ECHO_REQUEST && replyType != (byte) ICMP6_ECHO_REPLY)) {
- Log.i(TAG, "Received unexpected ICMP reply: original " + type
- + ", reply " + replyType);
- return;
- }
-
- // Compare the response we got with the original packet.
- // The only thing that should have changed are addresses, type and checksum.
- // Overwrite them with the received bytes and see if the packet is otherwise identical.
- request[hdrLen] = buf[hdrLen]; // Type
- request[hdrLen + 2] = buf[hdrLen + 2]; // Checksum byte 1.
- request[hdrLen + 3] = buf[hdrLen + 3]; // Checksum byte 2.
-
- // Since Linux kernel 4.2, net.ipv6.auto_flowlabels is set by default, and therefore
- // the request and reply may have different IPv6 flow label: ignore that as well.
- if (version == 6) {
- request[1] = (byte) (request[1] & 0xf0 | buf[1] & 0x0f);
- request[2] = buf[2];
- request[3] = buf[3];
- }
-
- for (int i = 0; i < len; i++) {
- if (buf[i] != request[i]) {
- Log.i(TAG, "Received non-matching packet when expecting ping response.");
- return;
- }
- }
-
- // Now swap the addresses again and reflect the packet. This sends a ping reply.
- swapAddresses(buf, version);
- writePacket(buf, len);
- }
-
- private void writePacket(@NonNull byte[] buf, int len) {
- try {
- Os.write(mFd, buf, 0, len);
- } catch (ErrnoException | IOException e) {
- Log.e(TAG, "Error writing packet: " + e.getMessage());
- }
- }
-
- // Reads one packet from our mFd, and possibly writes the packet back.
- private void processPacket() {
- int len = PacketReflectorUtil.readPacket(mFd, mBuf);
- if (len < 1) {
- // Usually happens when socket read is being interrupted, e.g. stopping PacketReflector.
- return;
- }
-
- int version = mBuf[0] >> 4;
- int protoPos, hdrLen;
- if (version == 4) {
- hdrLen = IPV4_HEADER_LENGTH;
- protoPos = IPV4_PROTO_OFFSET;
- } else if (version == 6) {
- hdrLen = IPV6_HEADER_LENGTH;
- protoPos = IPV6_PROTO_OFFSET;
- } else {
- throw new IllegalStateException("Unexpected version: " + version);
- }
-
- if (len < hdrLen) {
- throw new IllegalStateException("Unexpected buffer length: " + len);
- }
-
- byte proto = mBuf[protoPos];
- switch (proto) {
- case IPPROTO_ICMP:
- // fall through
- case IPPROTO_ICMPV6:
- processIcmpPacket(mBuf, version, len, hdrLen);
- break;
- case IPPROTO_TCP:
- processTcpPacket(mBuf, version, len, hdrLen);
- break;
- case IPPROTO_UDP:
- processUdpPacket(mBuf, version, len, hdrLen);
- break;
- }
- }
-
- public void run() {
- Log.i(TAG, "starting fd=" + mFd + " valid=" + mFd.valid());
- while (!interrupted() && mFd.valid()) {
- processPacket();
- }
- Log.i(TAG, "exiting fd=" + mFd + " valid=" + mFd.valid());
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/PacketReflectorUtil.kt b/common/testutils/devicetests/com/android/testutils/PacketReflectorUtil.kt
deleted file mode 100644
index 498b1a36..00000000
--- a/common/testutils/devicetests/com/android/testutils/PacketReflectorUtil.kt
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2023 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("PacketReflectorUtil")
-
-package com.android.testutils
-
-import android.system.ErrnoException
-import android.system.Os
-import android.system.OsConstants
-import com.android.net.module.util.IpUtils
-import com.android.testutils.PacketReflector.IPV4_HEADER_LENGTH
-import com.android.testutils.PacketReflector.IPV6_HEADER_LENGTH
-import java.io.FileDescriptor
-import java.io.InterruptedIOException
-import java.net.InetAddress
-import java.nio.ByteBuffer
-
-fun readPacket(fd: FileDescriptor, buf: ByteArray): Int {
- return try {
- Os.read(fd, buf, 0, buf.size)
- } catch (e: ErrnoException) {
- // Ignore normal use cases such as the EAGAIN error indicates that the read operation
- // cannot be completed immediately, or the EINTR error indicates that the read
- // operation was interrupted by a signal.
- if (e.errno == OsConstants.EAGAIN || e.errno == OsConstants.EINTR) {
- -1
- } else {
- throw e
- }
- } catch (e: InterruptedIOException) {
- -1
- }
-}
-
-fun getInetAddressAt(buf: ByteArray, pos: Int, len: Int): InetAddress =
- InetAddress.getByAddress(buf.copyOfRange(pos, pos + len))
-
-/**
- * Reads a 16-bit unsigned int at pos in big endian, with no alignment requirements.
- */
-fun getPortAt(buf: ByteArray, pos: Int): Int {
- return (buf[pos].toInt() and 0xff shl 8) + (buf[pos + 1].toInt() and 0xff)
-}
-
-fun setPortAt(port: Int, buf: ByteArray, pos: Int) {
- buf[pos] = (port ushr 8).toByte()
- buf[pos + 1] = (port and 0xff).toByte()
-}
-
-fun getAddressPositionAndLength(version: Int) = when (version) {
- 4 -> PacketReflector.IPV4_ADDR_OFFSET to PacketReflector.IPV4_ADDR_LENGTH
- 6 -> PacketReflector.IPV6_ADDR_OFFSET to PacketReflector.IPV6_ADDR_LENGTH
- else -> throw IllegalArgumentException("Unknown IP version $version")
-}
-
-private const val IPV4_CHKSUM_OFFSET = 10
-private const val UDP_CHECKSUM_OFFSET = 6
-private const val TCP_CHECKSUM_OFFSET = 16
-
-fun fixPacketChecksum(buf: ByteArray, len: Int, version: Int, protocol: Byte) {
- // Fill Ip checksum for IPv4. IPv6 header doesn't have a checksum field.
- if (version == 4) {
- val checksum = IpUtils.ipChecksum(ByteBuffer.wrap(buf), 0)
- // Place checksum in Big-endian order.
- buf[IPV4_CHKSUM_OFFSET] = (checksum.toInt() ushr 8).toByte()
- buf[IPV4_CHKSUM_OFFSET + 1] = (checksum.toInt() and 0xff).toByte()
- }
-
- // Fill transport layer checksum.
- val transportOffset = if (version == 4) IPV4_HEADER_LENGTH else IPV6_HEADER_LENGTH
- when (protocol) {
- PacketReflector.IPPROTO_UDP -> {
- val checksumPos = transportOffset + UDP_CHECKSUM_OFFSET
- // Clear before calculate.
- buf[checksumPos + 1] = 0x00
- buf[checksumPos] = buf[checksumPos + 1]
- val checksum = IpUtils.udpChecksum(
- ByteBuffer.wrap(buf), 0,
- transportOffset
- )
- buf[checksumPos] = (checksum.toInt() ushr 8).toByte()
- buf[checksumPos + 1] = (checksum.toInt() and 0xff).toByte()
- }
- PacketReflector.IPPROTO_TCP -> {
- val checksumPos = transportOffset + TCP_CHECKSUM_OFFSET
- // Clear before calculate.
- buf[checksumPos + 1] = 0x00
- buf[checksumPos] = buf[checksumPos + 1]
- val transportLen: Int = len - transportOffset
- val checksum = IpUtils.tcpChecksum(
- ByteBuffer.wrap(buf), 0, transportOffset,
- transportLen
- )
- buf[checksumPos] = (checksum.toInt() ushr 8).toByte()
- buf[checksumPos + 1] = (checksum.toInt() and 0xff).toByte()
- }
- // TODO: Support ICMP.
- else -> throw IllegalArgumentException("Unsupported protocol: $protocol")
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/PacketResponder.kt b/common/testutils/devicetests/com/android/testutils/PacketResponder.kt
deleted file mode 100644
index 964c6c60..00000000
--- a/common/testutils/devicetests/com/android/testutils/PacketResponder.kt
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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 java.util.function.Predicate
-
-private const val POLL_FREQUENCY_MS = 1000L
-
-/**
- * A class that can be used to reply to packets from a [TapPacketReader].
- *
- * A reply thread will be created to reply to incoming packets asynchronously.
- * The receiver creates a new read head on the [TapPacketReader], to read packets, so it does not
- * affect packets obtained through [TapPacketReader.popPacket].
- *
- * @param reader a [TapPacketReader] to obtain incoming packets and reply to them.
- * @param packetFilter A filter to apply to incoming packets.
- * @param name Name to use for the internal responder thread.
- */
-abstract class PacketResponder(
- private val reader: TapPacketReader,
- private val packetFilter: Predicate<ByteArray>,
- name: String
-) {
- private val replyThread = ReplyThread(name)
-
- protected abstract fun replyToPacket(packet: ByteArray, reader: TapPacketReader)
-
- /**
- * Start the [PacketResponder].
- */
- fun start() {
- replyThread.start()
- }
-
- /**
- * Stop the [PacketResponder].
- *
- * The responder cannot be used anymore after being stopped.
- */
- fun stop() {
- replyThread.interrupt()
- replyThread.join()
- }
-
- private inner class ReplyThread(name: String) : Thread(name) {
- override fun run() {
- try {
- // Create a new ReadHead so other packets polled on the reader are not affected
- val recvPackets = reader.receivedPackets.newReadHead()
- while (!isInterrupted) {
- recvPackets.poll(POLL_FREQUENCY_MS, packetFilter::test)?.let {
- replyToPacket(it, reader)
- }
- }
- } catch (e: InterruptedException) {
- // Exit gracefully
- }
- }
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/ParcelUtils.kt b/common/testutils/devicetests/com/android/testutils/ParcelUtils.kt
deleted file mode 100644
index 14ed8e9e..00000000
--- a/common/testutils/devicetests/com/android/testutils/ParcelUtils.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2019 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("ParcelUtils")
-
-package com.android.testutils
-
-import android.os.Parcel
-import android.os.Parcelable
-import kotlin.test.assertTrue
-import kotlin.test.fail
-
-/**
- * Return a new instance of `T` after being parceled then unparceled.
- */
-fun <T : Parcelable> parcelingRoundTrip(source: T): T {
- val creator: Parcelable.Creator<T>
- try {
- creator = source.javaClass.getField("CREATOR").get(null) as Parcelable.Creator<T>
- } catch (e: IllegalAccessException) {
- fail("Missing CREATOR field: " + e.message)
- } catch (e: NoSuchFieldException) {
- fail("Missing CREATOR field: " + e.message)
- }
-
- var p = Parcel.obtain()
- source.writeToParcel(p, /* flags */ 0)
- p.setDataPosition(0)
- val marshalled = p.marshall()
- p = Parcel.obtain()
- p.unmarshall(marshalled, 0, marshalled.size)
- p.setDataPosition(0)
- return creator.createFromParcel(p)
-}
-
-/**
- * Assert that after being parceled then unparceled, `source` is equal to the original
- * object. If a customized equals function is provided, uses the provided one.
- */
-@JvmOverloads
-fun <T : Parcelable> assertParcelingIsLossless(
- source: T,
- equals: (T, T) -> Boolean = { a, b -> a == b }
-) {
- val actual = parcelingRoundTrip(source)
- assertTrue(equals(source, actual), "Expected $source, but was $actual")
-}
-
-@JvmOverloads
-fun <T : Parcelable> assertParcelSane(
- obj: T,
- fieldCount: Int,
- equals: (T, T) -> Boolean = { a, b -> a == b }
-) {
- assertFieldCountEquals(fieldCount, obj::class.java)
- assertParcelingIsLossless(obj, equals)
-}
diff --git a/common/testutils/devicetests/com/android/testutils/RouterAdvertisementResponder.java b/common/testutils/devicetests/com/android/testutils/RouterAdvertisementResponder.java
deleted file mode 100644
index 51d57bc1..00000000
--- a/common/testutils/devicetests/com/android/testutils/RouterAdvertisementResponder.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2022 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 static android.system.OsConstants.IPPROTO_ICMPV6;
-
-import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6;
-import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_SLLA;
-import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_TLLA;
-import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NEIGHBOR_SOLICITATION;
-import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION;
-import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_NODES_MULTICAST;
-import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE;
-import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER;
-import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED;
-import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_AUTONOMOUS;
-import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_ON_LINK;
-
-import android.net.InetAddresses;
-import android.net.IpPrefix;
-import android.net.MacAddress;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.Pair;
-
-import com.android.net.module.util.Ipv6Utils;
-import com.android.net.module.util.Struct;
-import com.android.net.module.util.structs.EthernetHeader;
-import com.android.net.module.util.structs.Icmpv6Header;
-import com.android.net.module.util.structs.Ipv6Header;
-import com.android.net.module.util.structs.LlaOption;
-import com.android.net.module.util.structs.NsHeader;
-import com.android.net.module.util.structs.PrefixInformationOption;
-import com.android.net.module.util.structs.RdnssOption;
-
-import java.io.IOException;
-import java.net.Inet6Address;
-import java.nio.ByteBuffer;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Random;
-
-/**
- * ND (RA & NA) responder class useful for tests that require a provisioned IPv6 interface.
- * TODO: rename to NdResponder
- */
-public class RouterAdvertisementResponder extends PacketResponder {
- private static final String TAG = "RouterAdvertisementResponder";
- private static final Inet6Address DNS_SERVER =
- (Inet6Address) InetAddresses.parseNumericAddress("2001:4860:4860::64");
- private final TapPacketReader mPacketReader;
- // Maps IPv6 address to MacAddress and isRouter boolean.
- private final Map<Inet6Address, Pair<MacAddress, Boolean>> mNeighborMap = new ArrayMap<>();
- private final IpPrefix mPrefix;
-
- public RouterAdvertisementResponder(TapPacketReader packetReader, IpPrefix prefix) {
- super(packetReader, RouterAdvertisementResponder::isRsOrNs, TAG);
- mPacketReader = packetReader;
- mPrefix = Objects.requireNonNull(prefix);
- }
-
- public RouterAdvertisementResponder(TapPacketReader packetReader) {
- this(packetReader, makeRandomPrefix());
- }
-
- private static IpPrefix makeRandomPrefix() {
- final byte[] prefixBytes = new IpPrefix("2001:db8::/64").getAddress().getAddress();
- final Random r = new Random();
- for (int i = 4; i < 8; i++) {
- prefixBytes[i] = (byte) r.nextInt();
- }
- return new IpPrefix(prefixBytes, 64);
- }
-
- /** Returns true if the packet is a router solicitation or neighbor solicitation message. */
- private static boolean isRsOrNs(byte[] packet) {
- final ByteBuffer buffer = ByteBuffer.wrap(packet);
- final EthernetHeader ethHeader = Struct.parse(EthernetHeader.class, buffer);
- if (ethHeader.etherType != ETHER_TYPE_IPV6) {
- return false;
- }
- final Ipv6Header ipv6Header = Struct.parse(Ipv6Header.class, buffer);
- if (ipv6Header.nextHeader != IPPROTO_ICMPV6) {
- return false;
- }
- final Icmpv6Header icmpv6Header = Struct.parse(Icmpv6Header.class, buffer);
- return icmpv6Header.type == ICMPV6_ROUTER_SOLICITATION
- || icmpv6Header.type == ICMPV6_NEIGHBOR_SOLICITATION;
- }
-
- /**
- * Adds a new router to be advertised.
- * @param mac the mac address of the router.
- * @param ip the link-local address of the router.
- */
- public void addRouterEntry(MacAddress mac, Inet6Address ip) {
- mNeighborMap.put(ip, new Pair<>(mac, true));
- }
-
- /**
- * Adds a new neighbor to be advertised.
- * @param mac the mac address of the neighbor.
- * @param ip the link-local address of the neighbor.
- */
- public void addNeighborEntry(MacAddress mac, Inet6Address ip) {
- mNeighborMap.put(ip, new Pair<>(mac, false));
- }
-
- /**
- * @return the prefix that is announced in the Router Advertisements sent by this object.
- */
- public IpPrefix getPrefix() {
- return mPrefix;
- }
-
- private ByteBuffer buildPrefixOption() {
- return PrefixInformationOption.build(
- mPrefix, (byte) (PIO_FLAG_ON_LINK | PIO_FLAG_AUTONOMOUS),
- 3600 /* valid lifetime */, 3600 /* preferred lifetime */);
- }
-
- private ByteBuffer buildRdnssOption() {
- return RdnssOption.build(3600/*lifetime, must be at least 120*/, DNS_SERVER);
- }
-
- private ByteBuffer buildSllaOption(MacAddress srcMac) {
- return LlaOption.build((byte) ICMPV6_ND_OPTION_SLLA, srcMac);
- }
-
- private ByteBuffer buildRaPacket(MacAddress srcMac, MacAddress dstMac, Inet6Address srcIp) {
- return Ipv6Utils.buildRaPacket(srcMac, dstMac, srcIp, IPV6_ADDR_ALL_NODES_MULTICAST,
- (byte) 0 /*M=0, O=0*/, 3600 /*lifetime*/, 0 /*reachableTime, unspecified*/,
- 0/*retransTimer, unspecified*/, buildPrefixOption(), buildRdnssOption(),
- buildSllaOption(srcMac));
- }
-
- private static void sendResponse(TapPacketReader reader, ByteBuffer buffer) {
- try {
- reader.sendResponse(buffer);
- } catch (IOException e) {
- // Throwing an exception here will crash the test process. Let's stick to logging, as
- // the test will fail either way.
- Log.e(TAG, "Failed to send buffer", e);
- }
- }
-
- private void replyToRouterSolicitation(TapPacketReader reader, MacAddress dstMac) {
- for (Map.Entry<Inet6Address, Pair<MacAddress, Boolean>> it : mNeighborMap.entrySet()) {
- final boolean isRouter = it.getValue().second;
- if (!isRouter) {
- continue;
- }
- final ByteBuffer raResponse = buildRaPacket(it.getValue().first, dstMac, it.getKey());
- sendResponse(reader, raResponse);
- }
- }
-
- private void replyToNeighborSolicitation(TapPacketReader reader, MacAddress dstMac,
- Inet6Address dstIp, Inet6Address targetIp) {
- final Pair<MacAddress, Boolean> neighbor = mNeighborMap.get(targetIp);
- if (neighbor == null) {
- return;
- }
-
- final MacAddress srcMac = neighbor.first;
- final boolean isRouter = neighbor.second;
- int flags = NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED | NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE;
- if (isRouter) {
- flags |= NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER;
- }
-
- final ByteBuffer tlla = LlaOption.build((byte) ICMPV6_ND_OPTION_TLLA, srcMac);
- final ByteBuffer naResponse = Ipv6Utils.buildNaPacket(srcMac, dstMac, targetIp, dstIp,
- flags, targetIp, tlla);
- sendResponse(reader, naResponse);
- }
-
- @Override
- protected void replyToPacket(byte[] packet, TapPacketReader reader) {
- final ByteBuffer buf = ByteBuffer.wrap(packet);
- // Messages are filtered by parent class, so it is safe to assume that packet is either an
- // RS or NS.
- final EthernetHeader ethHdr = Struct.parse(EthernetHeader.class, buf);
- final Ipv6Header ipv6Hdr = Struct.parse(Ipv6Header.class, buf);
- final Icmpv6Header icmpv6Header = Struct.parse(Icmpv6Header.class, buf);
-
- if (icmpv6Header.type == ICMPV6_ROUTER_SOLICITATION) {
- replyToRouterSolicitation(reader, ethHdr.srcMac);
- } else if (icmpv6Header.type == ICMPV6_NEIGHBOR_SOLICITATION) {
- final NsHeader nsHeader = Struct.parse(NsHeader.class, buf);
- replyToNeighborSolicitation(reader, ethHdr.srcMac, ipv6Hdr.srcIp, nsHeader.target);
- }
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/TapPacketReader.java b/common/testutils/devicetests/com/android/testutils/TapPacketReader.java
deleted file mode 100644
index b25b9f21..00000000
--- a/common/testutils/devicetests/com/android/testutils/TapPacketReader.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * 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.os.Handler;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.net.module.util.ArrayTrackRecord;
-import com.android.net.module.util.PacketReader;
-
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.function.Predicate;
-
-import kotlin.Lazy;
-import kotlin.LazyKt;
-
-/**
- * A packet reader that runs on a TAP interface.
- *
- * It also implements facilities to reply to received packets.
- */
-public class TapPacketReader extends PacketReader {
- private final FileDescriptor mTapFd;
- private final ArrayTrackRecord<byte[]> mReceivedPackets = new ArrayTrackRecord<>();
- private final Lazy<ArrayTrackRecord<byte[]>.ReadHead> mReadHead =
- LazyKt.lazy(mReceivedPackets::newReadHead);
-
- public TapPacketReader(Handler h, FileDescriptor tapFd, int maxPacketSize) {
- super(h, maxPacketSize);
- 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;
- }
-
- @Override
- protected void handlePacket(byte[] recvbuf, int length) {
- final byte[] newPacket = Arrays.copyOf(recvbuf, length);
- if (!mReceivedPackets.add(newPacket)) {
- throw new AssertionError("More than " + Integer.MAX_VALUE + " packets outstanding!");
- }
- }
-
- /**
- * @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[] poll(long timeoutMs) {
- return mReadHead.getValue().poll(timeoutMs, packet -> true);
- }
-
- /**
- * Get the next packet that was received on the interface and matches the specified filter.
- */
- @Nullable
- public byte[] poll(long timeoutMs, @NonNull Predicate<byte[]> filter) {
- return mReadHead.getValue().poll(timeoutMs, filter::test);
- }
-
- /**
- * Get the {@link ArrayTrackRecord} that records all packets received by the reader since its
- * creation.
- */
- public ArrayTrackRecord<byte[]> getReceivedPackets() {
- return mReceivedPackets;
- }
-
- /*
- * Send a response on the TAP interface.
- *
- * The passed ByteBuffer is flipped after use.
- *
- * @param packet The packet to send.
- * @throws IOException if the interface can't be written to.
- */
- public void sendResponse(final ByteBuffer packet) throws IOException {
- try (FileOutputStream out = new FileOutputStream(mTapFd)) {
- byte[] packetBytes = new byte[packet.limit()];
- packet.get(packetBytes);
- packet.flip(); // So we can reuse it in the future.
- out.write(packetBytes);
- }
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/TapPacketReaderRule.kt b/common/testutils/devicetests/com/android/testutils/TapPacketReaderRule.kt
deleted file mode 100644
index 701666ca..00000000
--- a/common/testutils/devicetests/com/android/testutils/TapPacketReaderRule.kt
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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.Handler
-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.
- *
- * @param maxPacketSize Maximum size of packets read in the [TapPacketReader] buffer.
- * @param autoStart Whether to initialize the interface and start the reader automatically for every
- * test. If false, each test must either call start() and stop(), or be annotated
- * with TapPacketReaderTest before using the reader or interface.
- */
-class TapPacketReaderRule @JvmOverloads constructor(
- private val maxPacketSize: Int = 1500,
- private val autoStart: Boolean = true
-) : 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 with autoStart = true, 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 if the members are
- // referenced before they could be initialized (typically if autoStart is false and the test
- // does not call start or use @TapPacketReaderTest).
- lateinit var iface: TestNetworkInterface
- lateinit var reader: TapPacketReader
-
- @Volatile
- private var readerRunning = false
-
- /**
- * Indicates that the [TapPacketReaderRule] should initialize its [TestNetworkInterface] and
- * start the [TapPacketReader] before the test, and tear them down afterwards.
- *
- * For use when [TapPacketReaderRule] is created with autoStart = false.
- */
- annotation class TapPacketReaderTest
-
- /**
- * Initialize the tap interface and start the [TapPacketReader].
- *
- * Tests using this method must also call [stop] before exiting.
- * @param handler Handler to run the reader on. Callers are responsible for safely terminating
- * the handler when the test ends. If null, a handler thread managed by the
- * rule will be used.
- */
- @JvmOverloads
- fun start(handler: Handler? = null) {
- if (this::iface.isInitialized) {
- fail("${TapPacketReaderRule::class.java.simpleName} was already started")
- }
-
- val ctx = InstrumentationRegistry.getInstrumentation().context
- iface = runAsShell(MANAGE_TEST_NETWORKS) {
- val tnm = ctx.getSystemService(TestNetworkManager::class.java)
- ?: fail("Could not obtain the TestNetworkManager")
- tnm.createTapInterface()
- }
- val usedHandler = handler ?: HandlerThread(
- TapPacketReaderRule::class.java.simpleName).apply { start() }.threadHandler
- reader = TapPacketReader(usedHandler, iface.fileDescriptor.fileDescriptor, maxPacketSize)
- reader.startAsyncForTest()
- readerRunning = true
- }
-
- /**
- * Stop the [TapPacketReader].
- *
- * Tests calling [start] must call this method before exiting. If a handler was specified in
- * [start], all messages on that handler must also be processed after calling this method and
- * before exiting.
- *
- * If [start] was not called, calling this method is a no-op.
- */
- fun stop() {
- // The reader may not be initialized if the test case did not use the rule, even though
- // other test cases in the same class may be using it (so test classes may call stop in
- // tearDown even if start is not called for all test cases).
- if (!this::reader.isInitialized) return
- reader.handler.post {
- reader.stop()
- readerRunning = false
- }
- }
-
- override fun apply(base: Statement, description: Description): Statement {
- return TapReaderStatement(base, description)
- }
-
- private inner class TapReaderStatement(
- private val base: Statement,
- private val description: Description
- ) : Statement() {
- override fun evaluate() {
- val shouldStart = autoStart ||
- description.getAnnotation(TapPacketReaderTest::class.java) != null
- if (shouldStart) {
- start()
- }
-
- try {
- base.evaluate()
- } finally {
- if (shouldStart) {
- stop()
- reader.handler.looper.apply {
- quitSafely()
- thread.join(HANDLER_TIMEOUT_MS)
- assertFalse(thread.isAlive,
- "HandlerThread did not exit within $HANDLER_TIMEOUT_MS ms")
- }
- }
-
- if (this@TapPacketReaderRule::iface.isInitialized) {
- iface.fileDescriptor.close()
- }
- }
-
- assertFalse(readerRunning,
- "stop() was not called, or the provided handler did not process the stop " +
- "message before the test ended. If not using autostart, make sure to call " +
- "stop() after the test. If a handler is specified in start(), make sure all " +
- "messages are processed after calling stop(), before quitting (for example " +
- "by using HandlerThread#quitSafely and HandlerThread#join).")
- }
- }
-} \ No newline at end of file
diff --git a/common/testutils/devicetests/com/android/testutils/TestBpfMap.java b/common/testutils/devicetests/com/android/testutils/TestBpfMap.java
deleted file mode 100644
index 733bd980..00000000
--- a/common/testutils/devicetests/com/android/testutils/TestBpfMap.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2022 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.system.ErrnoException;
-
-import androidx.annotation.NonNull;
-
-import com.android.net.module.util.IBpfMap;
-import com.android.net.module.util.Struct;
-
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- *
- * Fake BPF map class for tests that have no privilege to access real BPF maps. TestBpfMap does not
- * load JNI and all member functions do not access real BPF maps.
- *
- * Implements IBpfMap so that any class using IBpfMap can use this class in its tests.
- *
- * @param <K> the key type
- * @param <V> the value type
- */
-public class TestBpfMap<K extends Struct, V extends Struct> implements IBpfMap<K, V> {
- private final ConcurrentHashMap<K, V> mMap = new ConcurrentHashMap<>();
-
- // TODO: Remove this constructor
- public TestBpfMap(final Class<K> key, final Class<V> value) {
- }
-
- @Override
- public void forEach(ThrowingBiConsumer<K, V> action) throws ErrnoException {
- // TODO: consider using mocked #getFirstKey and #getNextKey to iterate. It helps to
- // implement the entry deletion in the iteration if required.
- for (Map.Entry<K, V> entry : mMap.entrySet()) {
- action.accept(entry.getKey(), entry.getValue());
- }
- }
-
- @Override
- public void updateEntry(K key, V value) throws ErrnoException {
- mMap.put(key, value);
- }
-
- @Override
- public void insertEntry(K key, V value) throws ErrnoException,
- IllegalArgumentException {
- // The entry is created if and only if it doesn't exist. See BpfMap#insertEntry.
- if (mMap.get(key) != null) {
- throw new IllegalArgumentException(key + " already exist");
- }
- mMap.put(key, value);
- }
-
- @Override
- public void replaceEntry(K key, V value) throws ErrnoException, NoSuchElementException {
- if (!mMap.containsKey(key)) throw new NoSuchElementException();
- mMap.put(key, value);
- }
-
- @Override
- public boolean insertOrReplaceEntry(K key, V value) throws ErrnoException {
- // Returns true if inserted, false if replaced.
- boolean ret = !mMap.containsKey(key);
- mMap.put(key, value);
- return ret;
- }
-
- @Override
- public boolean deleteEntry(Struct key) throws ErrnoException {
- return mMap.remove(key) != null;
- }
-
- @Override
- public boolean isEmpty() throws ErrnoException {
- return mMap.isEmpty();
- }
-
- @Override
- public K getNextKey(@NonNull K key) {
- // Expensive, but since this is only for tests...
- Iterator<K> it = mMap.keySet().iterator();
- while (it.hasNext()) {
- if (Objects.equals(it.next(), key)) {
- return it.hasNext() ? it.next() : null;
- }
- }
- return null;
- }
-
- @Override
- public K getFirstKey() {
- for (K key : mMap.keySet()) {
- return key;
- }
- return null;
- }
-
- @Override
- public boolean containsKey(@NonNull K key) throws ErrnoException {
- return mMap.containsKey(key);
- }
-
- @Override
- public V getValue(@NonNull K key) throws ErrnoException {
- // Return value for a given key. Otherwise, return null without an error ENOENT.
- // BpfMap#getValue treats that the entry is not found as no error.
- return mMap.get(key);
- }
-
- @Override
- public void clear() throws ErrnoException {
- // TODO: consider using mocked #getFirstKey and #deleteEntry to implement.
- mMap.clear();
- }
-
- @Override
- public void close() throws IOException {
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/TestDnsServer.kt b/common/testutils/devicetests/com/android/testutils/TestDnsServer.kt
deleted file mode 100644
index e1b771b2..00000000
--- a/common/testutils/devicetests/com/android/testutils/TestDnsServer.kt
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2022 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.net.Network
-import android.util.Log
-import com.android.internal.annotations.GuardedBy
-import com.android.internal.annotations.VisibleForTesting
-import com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE
-import com.android.net.module.util.DnsPacket
-import java.net.DatagramPacket
-import java.net.DatagramSocket
-import java.net.InetAddress
-import java.net.InetSocketAddress
-import java.net.SocketAddress
-import java.net.SocketException
-import java.util.ArrayList
-
-private const val TAG = "TestDnsServer"
-private const val VDBG = true
-@VisibleForTesting(visibility = PRIVATE)
-const val MAX_BUF_SIZE = 8192
-
-/**
- * A simple implementation of Dns Server that can be bound on specific address and Network.
- *
- * The caller should use start() to make the server start a new thread to receive DNS queries
- * on the bound address, [isAlive] to check status, and stop() for stopping.
- * The server allows user to manipulate the records to be answered through
- * [setAnswer] at runtime.
- *
- * This server runs on its own thread. Please make sure writing the query to the socket
- * happens-after using [setAnswer] to guarantee the correct answer is returned. If possible,
- * use [setAnswer] before calling [start] for simplicity.
- */
-class TestDnsServer(network: Network, addr: InetSocketAddress) {
- enum class Status {
- NOT_STARTED, STARTED, STOPPED
- }
- @GuardedBy("thread")
- private var status: Status = Status.NOT_STARTED
- private val thread = ReceivingThread()
- private val socket = DatagramSocket(addr).also { network.bindSocket(it) }
- private val ansProvider = DnsAnswerProvider()
-
- // The buffer to store the received packet. They are being reused for
- // efficiency and it's fine because they are only ever accessed
- // on the server thread in a sequential manner.
- private val buffer = ByteArray(MAX_BUF_SIZE)
- private val packet = DatagramPacket(buffer, buffer.size)
-
- fun setAnswer(hostname: String, answer: List<InetAddress>) =
- ansProvider.setAnswer(hostname, answer)
-
- private fun processPacket() {
- // Blocking read and try construct a DnsQueryPacket object.
- socket.receive(packet)
- val q = DnsQueryPacket(packet.data)
- handleDnsQuery(q, packet.socketAddress)
- }
-
- // TODO: Add support to reply some error with a DNS reply packet with failure RCODE.
- private fun handleDnsQuery(q: DnsQueryPacket, src: SocketAddress) {
- val queryRecords = q.queryRecords
- if (queryRecords.size != 1) {
- throw IllegalArgumentException(
- "Expected one dns query record but got ${queryRecords.size}"
- )
- }
- val answerRecords = queryRecords[0].let { ansProvider.getAnswer(it.dName, it.nsType) }
-
- if (VDBG) {
- Log.v(TAG, "handleDnsPacket: " +
- queryRecords.map { "${it.dName},${it.nsType}" }.joinToString() +
- " ansCount=${answerRecords.size} socketAddress=$src")
- }
-
- val bytes = q.getAnswerPacket(answerRecords).bytes
- val reply = DatagramPacket(bytes, bytes.size, src)
- socket.send(reply)
- }
-
- fun start() {
- synchronized(thread) {
- if (status != Status.NOT_STARTED) {
- throw IllegalStateException("unexpected status: $status")
- }
- thread.start()
- status = Status.STARTED
- }
- }
- fun stop() {
- synchronized(thread) {
- if (status != Status.STARTED) {
- throw IllegalStateException("unexpected status: $status")
- }
- // The thread needs to be interrupted before closing the socket to prevent a data
- // race where the thread tries to read from the socket while it's being closed.
- // DatagramSocket is not thread-safe and running both concurrently can end up in
- // getPort() returning -1 after it's been checked not to, resulting in a crash by
- // IllegalArgumentException inside the DatagramSocket implementation.
- thread.interrupt()
- socket.close()
- thread.join()
- status = Status.STOPPED
- }
- }
- val isAlive get() = thread.isAlive
- val port get() = socket.localPort
-
- inner class ReceivingThread : Thread() {
- override fun run() {
- while (!interrupted() && !socket.isClosed) {
- try {
- processPacket()
- } catch (e: InterruptedException) {
- // The caller terminated the server, exit.
- break
- } catch (e: SocketException) {
- // The caller terminated the server, exit.
- break
- }
- }
- Log.i(TAG, "exiting socket={$socket}")
- }
- }
-
- @VisibleForTesting(visibility = PRIVATE)
- class DnsQueryPacket : DnsPacket {
- constructor(data: ByteArray) : super(data)
- constructor(header: DnsHeader, qd: List<DnsRecord>, an: List<DnsRecord>) :
- super(header, qd, an)
-
- init {
- if (mHeader.isResponse) {
- throw ParseException("Not a query packet")
- }
- }
-
- val queryRecords: List<DnsRecord>
- get() = mRecords[QDSECTION]
-
- fun getAnswerPacket(ar: List<DnsRecord>): DnsAnswerPacket {
- // Set QR bit of flag to 1 for response packet according to RFC 1035 section 4.1.1.
- val flags = 1 shl 15
- val qr = ArrayList(mRecords[QDSECTION])
- // Copy the query packet header id to the answer packet as RFC 1035 section 4.1.1.
- val header = DnsHeader(mHeader.id, flags, qr.size, ar.size)
- return DnsAnswerPacket(header, qr, ar)
- }
- }
-
- class DnsAnswerPacket : DnsPacket {
- constructor(header: DnsHeader, qr: List<DnsRecord>, ar: List<DnsRecord>) :
- super(header, qr, ar)
- @VisibleForTesting(visibility = PRIVATE)
- constructor(bytes: ByteArray) : super(bytes)
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/TestHttpServer.kt b/common/testutils/devicetests/com/android/testutils/TestHttpServer.kt
deleted file mode 100644
index 740bf63a..00000000
--- a/common/testutils/devicetests/com/android/testutils/TestHttpServer.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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.net.Uri
-import com.android.net.module.util.ArrayTrackRecord
-import fi.iki.elonen.NanoHTTPD
-import java.io.IOException
-
-/**
- * A minimal HTTP server running on a random available port.
- *
- * @param host The host to listen to, or null to listen on all hosts
- */
-class TestHttpServer(host: String? = null) : NanoHTTPD(host, 0 /* auto-select the port */) {
- // Map of URL path -> HTTP response code
- private val responses = HashMap<Request, Response>()
-
- /**
- * A record of all requests received by the server since it was started.
- */
- val requestsRecord = ArrayTrackRecord<Request>()
-
- /**
- * A request received by the test server.
- */
- data class Request(
- val path: String,
- val method: Method = Method.GET,
- val queryParameters: String = ""
- ) {
- /**
- * Returns whether the specified [Uri] matches parameters of this request.
- */
- fun matches(uri: Uri) = (uri.path ?: "") == path && (uri.query ?: "") == queryParameters
- }
-
- /**
- * Add a response for GET requests with the path and query parameters of the specified [Uri].
- */
- fun addResponse(
- uri: Uri,
- statusCode: Response.IStatus,
- headers: Map<String, String>? = null,
- content: String = ""
- ) {
- addResponse(Request(uri.path
- ?: "", Method.GET, uri.query ?: ""),
- statusCode, headers, content)
- }
-
- /**
- * Add a response for the given request.
- */
- fun addResponse(
- request: Request,
- statusCode: Response.IStatus,
- headers: Map<String, String>? = null,
- content: String = ""
- ) {
- val response = newFixedLengthResponse(statusCode, "text/plain", content)
- headers?.forEach {
- (key, value) -> response.addHeader(key, value)
- }
- responses[request] = response
- }
-
- override fun serve(session: IHTTPSession): Response {
- val request = Request(session.uri
- ?: "", session.method, session.queryParameterString ?: "")
- requestsRecord.add(request)
-
- // For PUT and POST, call parseBody to read InputStream before responding.
- if (Method.PUT == session.method || Method.POST == session.method) {
- try {
- session.parseBody(HashMap())
- } catch (e: Exception) {
- when (e) {
- is IOException, is ResponseException -> e.toResponse()
- else -> throw e
- }
- }
- }
-
- // Default response is a 404
- return responses[request] ?: super.serve(session)
- }
-
- fun Exception.toResponse() =
- newFixedLengthResponse(Response.Status.INTERNAL_ERROR, "text/plain", this.toString())
-}
diff --git a/common/testutils/devicetests/com/android/testutils/TestNetworkTracker.kt b/common/testutils/devicetests/com/android/testutils/TestNetworkTracker.kt
deleted file mode 100644
index 84fb47bc..00000000
--- a/common/testutils/devicetests/com/android/testutils/TestNetworkTracker.kt
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * 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.content.Context
-import android.net.ConnectivityManager
-import android.net.ConnectivityManager.NetworkCallback
-import android.net.LinkAddress
-import android.net.Network
-import android.net.NetworkCapabilities
-import android.net.NetworkRequest
-import android.net.LinkProperties
-import android.net.TestNetworkInterface
-import android.net.TestNetworkManager
-import android.os.Binder
-import android.os.Build
-import androidx.annotation.RequiresApi
-import com.android.modules.utils.build.SdkLevel.isAtLeastR
-import com.android.modules.utils.build.SdkLevel.isAtLeastS
-import java.util.concurrent.CompletableFuture
-import java.util.concurrent.TimeUnit
-import kotlin.test.assertTrue
-
-/**
- * Create a test network based on a TUN interface with a LinkAddress.
- *
- * TODO: remove this function after fixing all the callers to use a list of LinkAddresses.
- * This method will block until the test network is available. Requires
- * [android.Manifest.permission.CHANGE_NETWORK_STATE] and
- * [android.Manifest.permission.MANAGE_TEST_NETWORKS].
- */
-fun initTestNetwork(
- context: Context,
- interfaceAddr: LinkAddress,
- setupTimeoutMs: Long = 10_000L
-): TestNetworkTracker {
- return initTestNetwork(context, listOf(interfaceAddr), setupTimeoutMs)
-}
-
-/**
- * Create a test network based on a TUN interface with a LinkAddress list.
- *
- * This method will block until the test network is available. Requires
- * [android.Manifest.permission.CHANGE_NETWORK_STATE] and
- * [android.Manifest.permission.MANAGE_TEST_NETWORKS].
- */
-fun initTestNetwork(
- context: Context,
- linkAddrs: List<LinkAddress>,
- setupTimeoutMs: Long = 10_000L
-): TestNetworkTracker {
- return initTestNetwork(context, linkAddrs, lp = null, setupTimeoutMs = setupTimeoutMs)
-}
-
-/**
- * Create a test network based on a TUN interface
- *
- * This method will block until the test network is available. Requires
- * [android.Manifest.permission.CHANGE_NETWORK_STATE] and
- * [android.Manifest.permission.MANAGE_TEST_NETWORKS].
- *
- * This is only usable starting from R as [TestNetworkManager] has no support for specifying
- * LinkProperties on Q.
- */
-@RequiresApi(Build.VERSION_CODES.R)
-fun initTestNetwork(
- context: Context,
- lp: LinkProperties,
- setupTimeoutMs: Long = 10_000L
-): TestNetworkTracker {
- return initTestNetwork(context, lp.linkAddresses, lp, setupTimeoutMs)
-}
-
-private fun initTestNetwork(
- context: Context,
- linkAddrs: List<LinkAddress>,
- lp: LinkProperties?,
- setupTimeoutMs: Long = 10_000L
-): TestNetworkTracker {
- val tnm = context.getSystemService(TestNetworkManager::class.java)!!
- val iface = if (isAtLeastS()) tnm.createTunInterface(linkAddrs)
- else tnm.createTunInterface(linkAddrs.toTypedArray())
- val lpWithIface = if (lp == null) null else LinkProperties(lp).apply {
- interfaceName = iface.interfaceName
- }
- return TestNetworkTracker(context, iface, tnm, lpWithIface, setupTimeoutMs)
-}
-
-/**
- * Utility class to create and track test networks.
- *
- * This class is not thread-safe.
- */
-class TestNetworkTracker internal constructor(
- val context: Context,
- val iface: TestNetworkInterface,
- val tnm: TestNetworkManager,
- val lp: LinkProperties?,
- setupTimeoutMs: Long
-) : TestableNetworkCallback.HasNetwork {
- private val cm = context.getSystemService(ConnectivityManager::class.java)!!
- private val binder = Binder()
-
- private val networkCallback: NetworkCallback
- override val network: Network
- val testIface: TestNetworkInterface
-
- init {
- val networkFuture = CompletableFuture<Network>()
- val networkRequest = NetworkRequest.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
- // Test networks do not have NOT_VPN or TRUSTED capabilities by default
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
- .setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(iface.interfaceName))
- .build()
- networkCallback = object : NetworkCallback() {
- override fun onAvailable(network: Network) {
- networkFuture.complete(network)
- }
- }
- cm.requestNetwork(networkRequest, networkCallback)
-
- network = try {
- if (lp != null) {
- assertTrue(isAtLeastR(), "Cannot specify TestNetwork LinkProperties before R")
- tnm.setupTestNetwork(lp, true /* isMetered */, binder)
- } else {
- tnm.setupTestNetwork(iface.interfaceName, binder)
- }
- networkFuture.get(setupTimeoutMs, TimeUnit.MILLISECONDS)
- } catch (e: Throwable) {
- cm.unregisterNetworkCallback(networkCallback)
- throw e
- }
-
- testIface = iface
- }
-
- fun teardown() {
- cm.unregisterNetworkCallback(networkCallback)
- tnm.teardownTestNetwork(network)
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/TestPermissionUtil.kt b/common/testutils/devicetests/com/android/testutils/TestPermissionUtil.kt
deleted file mode 100644
index f571f64f..00000000
--- a/common/testutils/devicetests/com/android/testutils/TestPermissionUtil.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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.modules.utils.build.SdkLevel
-import com.android.testutils.FunctionalUtils.ThrowingRunnable
-import com.android.testutils.FunctionalUtils.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
-
- // Calls to adoptShellPermissionIdentity do not nest, and dropShellPermissionIdentity drops all
- // permissions. Thus, nesting calls will almost certainly cause test bugs, On S+, where we can
- // detect this, refuse to do it.
- //
- // TODO: when R is deprecated, we could try to make this work instead.
- // - Get the list of previously-adopted permissions.
- // - Adopt the union of the previously-adopted and newly-requested permissions.
- // - Run the task.
- // - Adopt the previously-adopted permissions, dropping the ones just adopted.
- //
- // This would allow tests (and utility classes, such as the TestCarrierConfigReceiver attempted
- // in aosp/2106007) to call runAsShell even within a test that has already adopted permissions.
- if (SdkLevel.isAtLeastS() && !autom.getAdoptedShellPermissions().isNullOrEmpty()) {
- throw IllegalStateException("adoptShellPermissionIdentity calls must not be nested")
- }
-
- 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
diff --git a/common/testutils/devicetests/com/android/testutils/TestableNetworkAgent.kt b/common/testutils/devicetests/com/android/testutils/TestableNetworkAgent.kt
deleted file mode 100644
index 8dc1bc45..00000000
--- a/common/testutils/devicetests/com/android/testutils/TestableNetworkAgent.kt
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2021 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.content.Context
-import android.net.KeepalivePacketData
-import android.net.LinkProperties
-import android.net.NetworkAgent
-import android.net.NetworkAgentConfig
-import android.net.NetworkCapabilities
-import android.net.NetworkProvider
-import android.net.QosFilter
-import android.net.Uri
-import android.os.Looper
-import com.android.net.module.util.ArrayTrackRecord
-import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnAddKeepalivePacketFilter
-import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnAutomaticReconnectDisabled
-import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnBandwidthUpdateRequested
-import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnDscpPolicyStatusUpdated
-import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkCreated
-import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkDestroyed
-import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkUnwanted
-import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnRegisterQosCallback
-import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnRemoveKeepalivePacketFilter
-import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnSaveAcceptUnvalidated
-import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnSignalStrengthThresholdsUpdated
-import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnStartSocketKeepalive
-import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnStopSocketKeepalive
-import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnUnregisterQosCallback
-import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnValidationStatus
-import java.time.Duration
-import kotlin.test.assertEquals
-import kotlin.test.assertNotNull
-import kotlin.test.assertNull
-import kotlin.test.assertTrue
-import org.junit.Assert.assertArrayEquals
-
-// Any legal score (0~99) for the test network would do, as it is going to be kept up by the
-// requests filed by the test and should never match normal internet requests. 70 is the default
-// score of Ethernet networks, it's as good a value as any other.
-private const val TEST_NETWORK_SCORE = 70
-
-private class Provider(context: Context, looper: Looper) :
- NetworkProvider(context, looper, "NetworkAgentTest NetworkProvider")
-
-public open class TestableNetworkAgent(
- context: Context,
- looper: Looper,
- val nc: NetworkCapabilities,
- val lp: LinkProperties,
- conf: NetworkAgentConfig
-) : NetworkAgent(context, looper, TestableNetworkAgent::class.java.simpleName /* tag */,
- nc, lp, TEST_NETWORK_SCORE, conf, Provider(context, looper)) {
-
- val DEFAULT_TIMEOUT_MS = 5000L
-
- val history = ArrayTrackRecord<CallbackEntry>().newReadHead()
-
- sealed class CallbackEntry {
- object OnBandwidthUpdateRequested : CallbackEntry()
- object OnNetworkUnwanted : CallbackEntry()
- data class OnAddKeepalivePacketFilter(
- val slot: Int,
- val packet: KeepalivePacketData
- ) : CallbackEntry()
- data class OnRemoveKeepalivePacketFilter(val slot: Int) : CallbackEntry()
- data class OnStartSocketKeepalive(
- val slot: Int,
- val interval: Int,
- val packet: KeepalivePacketData
- ) : CallbackEntry()
- data class OnStopSocketKeepalive(val slot: Int) : CallbackEntry()
- data class OnSaveAcceptUnvalidated(val accept: Boolean) : CallbackEntry()
- object OnAutomaticReconnectDisabled : CallbackEntry()
- data class OnValidationStatus(val status: Int, val uri: Uri?) : CallbackEntry()
- data class OnSignalStrengthThresholdsUpdated(val thresholds: IntArray) : CallbackEntry()
- object OnNetworkCreated : CallbackEntry()
- object OnNetworkDestroyed : CallbackEntry()
- data class OnDscpPolicyStatusUpdated(val policyId: Int, val status: Int) : CallbackEntry()
- data class OnRegisterQosCallback(
- val callbackId: Int,
- val filter: QosFilter
- ) : CallbackEntry()
- data class OnUnregisterQosCallback(val callbackId: Int) : CallbackEntry()
- }
-
- override fun onBandwidthUpdateRequested() {
- history.add(OnBandwidthUpdateRequested)
- }
-
- override fun onNetworkUnwanted() {
- history.add(OnNetworkUnwanted)
- }
-
- override fun onAddKeepalivePacketFilter(slot: Int, packet: KeepalivePacketData) {
- history.add(OnAddKeepalivePacketFilter(slot, packet))
- }
-
- override fun onRemoveKeepalivePacketFilter(slot: Int) {
- history.add(OnRemoveKeepalivePacketFilter(slot))
- }
-
- override fun onStartSocketKeepalive(
- slot: Int,
- interval: Duration,
- packet: KeepalivePacketData
- ) {
- history.add(OnStartSocketKeepalive(slot, interval.seconds.toInt(), packet))
- }
-
- override fun onStopSocketKeepalive(slot: Int) {
- history.add(OnStopSocketKeepalive(slot))
- }
-
- override fun onSaveAcceptUnvalidated(accept: Boolean) {
- history.add(OnSaveAcceptUnvalidated(accept))
- }
-
- override fun onAutomaticReconnectDisabled() {
- history.add(OnAutomaticReconnectDisabled)
- }
-
- override fun onSignalStrengthThresholdsUpdated(thresholds: IntArray) {
- history.add(OnSignalStrengthThresholdsUpdated(thresholds))
- }
-
- fun expectSignalStrengths(thresholds: IntArray? = intArrayOf()) {
- expectCallback<OnSignalStrengthThresholdsUpdated>().let {
- assertArrayEquals(thresholds, it.thresholds)
- }
- }
-
- override fun onQosCallbackRegistered(qosCallbackId: Int, filter: QosFilter) {
- history.add(OnRegisterQosCallback(qosCallbackId, filter))
- }
-
- override fun onQosCallbackUnregistered(qosCallbackId: Int) {
- history.add(OnUnregisterQosCallback(qosCallbackId))
- }
-
- override fun onValidationStatus(status: Int, uri: Uri?) {
- history.add(OnValidationStatus(status, uri))
- }
-
- override fun onNetworkCreated() {
- history.add(OnNetworkCreated)
- }
-
- override fun onNetworkDestroyed() {
- history.add(OnNetworkDestroyed)
- }
-
- override fun onDscpPolicyStatusUpdated(policyId: Int, status: Int) {
- history.add(OnDscpPolicyStatusUpdated(policyId, status))
- }
-
- // Expects the initial validation event that always occurs immediately after registering
- // a NetworkAgent whose network does not require validation (which test networks do
- // not, since they lack the INTERNET capability). It always contains the default argument
- // for the URI.
- fun expectValidationBypassedStatus() = expectCallback<OnValidationStatus>().let {
- assertEquals(it.status, VALID_NETWORK)
- // The returned Uri is parsed from the empty string, which means it's an
- // instance of the (private) Uri.StringUri. There are no real good ways
- // to check this, the least bad is to just convert it to a string and
- // make sure it's empty.
- assertEquals("", it.uri.toString())
- }
-
- inline fun <reified T : CallbackEntry> expectCallback(): T {
- val foundCallback = history.poll(DEFAULT_TIMEOUT_MS)
- assertTrue(foundCallback is T, "Expected ${T::class} but found $foundCallback")
- return foundCallback
- }
-
- inline fun <reified T : CallbackEntry> expectCallback(valid: (T) -> Boolean) {
- val foundCallback = history.poll(DEFAULT_TIMEOUT_MS)
- assertTrue(foundCallback is T, "Expected ${T::class} but found $foundCallback")
- assertTrue(valid(foundCallback), "Unexpected callback : $foundCallback")
- }
-
- inline fun <reified T : CallbackEntry> eventuallyExpect() =
- history.poll(DEFAULT_TIMEOUT_MS) { it is T }.also {
- assertNotNull(it, "Callback ${T::class} not received")
- } as T
-
- fun assertNoCallback() {
- assertTrue(waitForIdle(DEFAULT_TIMEOUT_MS),
- "Handler didn't became idle after ${DEFAULT_TIMEOUT_MS}ms")
- assertNull(history.peek())
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt b/common/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt
deleted file mode 100644
index df9c61ae..00000000
--- a/common/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt
+++ /dev/null
@@ -1,569 +0,0 @@
-/*
- * Copyright (C) 2019 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.net.ConnectivityManager.NetworkCallback
-import android.net.LinkProperties
-import android.net.Network
-import android.net.NetworkCapabilities
-import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
-import android.util.Log
-import com.android.net.module.util.ArrayTrackRecord
-import com.android.testutils.RecorderCallback.CallbackEntry.Available
-import com.android.testutils.RecorderCallback.CallbackEntry.BlockedStatus
-import com.android.testutils.RecorderCallback.CallbackEntry.BlockedStatusInt
-import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
-import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
-import com.android.testutils.RecorderCallback.CallbackEntry.Losing
-import com.android.testutils.RecorderCallback.CallbackEntry.Lost
-import com.android.testutils.RecorderCallback.CallbackEntry.Resumed
-import com.android.testutils.RecorderCallback.CallbackEntry.Suspended
-import com.android.testutils.RecorderCallback.CallbackEntry.Unavailable
-import kotlin.reflect.KClass
-import kotlin.test.assertEquals
-import kotlin.test.assertNotNull
-import kotlin.test.fail
-
-object NULL_NETWORK : Network(-1)
-object ANY_NETWORK : Network(-2)
-fun anyNetwork() = ANY_NETWORK
-
-open class RecorderCallback private constructor(
- private val backingRecord: ArrayTrackRecord<CallbackEntry>
-) : NetworkCallback() {
- public constructor() : this(ArrayTrackRecord())
- protected constructor(src: RecorderCallback?) : this(src?.backingRecord ?: ArrayTrackRecord())
-
- private val TAG = this::class.simpleName
-
- sealed class CallbackEntry {
- // To get equals(), hashcode(), componentN() etc for free, the child classes of
- // this class are data classes. But while data classes can inherit from other classes,
- // they may only have visible members in the constructors, so they couldn't declare
- // a constructor with a non-val arg to pass to CallbackEntry. Instead, force all
- // subclasses to implement a `network' property, which can be done in a data class
- // constructor by specifying override.
- abstract val network: Network
-
- data class Available(override val network: Network) : CallbackEntry()
- data class CapabilitiesChanged(
- override val network: Network,
- val caps: NetworkCapabilities
- ) : CallbackEntry()
- data class LinkPropertiesChanged(
- override val network: Network,
- val lp: LinkProperties
- ) : CallbackEntry()
- data class Suspended(override val network: Network) : CallbackEntry()
- data class Resumed(override val network: Network) : CallbackEntry()
- data class Losing(override val network: Network, val maxMsToLive: Int) : CallbackEntry()
- data class Lost(override val network: Network) : CallbackEntry()
- data class Unavailable private constructor(
- override val network: Network
- ) : CallbackEntry() {
- constructor() : this(NULL_NETWORK)
- }
- data class BlockedStatus(
- override val network: Network,
- val blocked: Boolean
- ) : CallbackEntry()
- data class BlockedStatusInt(
- override val network: Network,
- val reason: Int
- ) : CallbackEntry()
- // Convenience constants for expecting a type
- companion object {
- @JvmField
- val AVAILABLE = Available::class
- @JvmField
- val NETWORK_CAPS_UPDATED = CapabilitiesChanged::class
- @JvmField
- val LINK_PROPERTIES_CHANGED = LinkPropertiesChanged::class
- @JvmField
- val SUSPENDED = Suspended::class
- @JvmField
- val RESUMED = Resumed::class
- @JvmField
- val LOSING = Losing::class
- @JvmField
- val LOST = Lost::class
- @JvmField
- val UNAVAILABLE = Unavailable::class
- @JvmField
- val BLOCKED_STATUS = BlockedStatus::class
- @JvmField
- val BLOCKED_STATUS_INT = BlockedStatusInt::class
- }
- }
-
- val history = backingRecord.newReadHead()
- val mark get() = history.mark
-
- override fun onAvailable(network: Network) {
- Log.d(TAG, "onAvailable $network")
- history.add(Available(network))
- }
-
- // PreCheck is not used in the tests today. For backward compatibility with existing tests that
- // expect the callbacks not to record this, do not listen to PreCheck here.
-
- override fun onCapabilitiesChanged(network: Network, caps: NetworkCapabilities) {
- Log.d(TAG, "onCapabilitiesChanged $network $caps")
- history.add(CapabilitiesChanged(network, caps))
- }
-
- override fun onLinkPropertiesChanged(network: Network, lp: LinkProperties) {
- Log.d(TAG, "onLinkPropertiesChanged $network $lp")
- history.add(LinkPropertiesChanged(network, lp))
- }
-
- override fun onBlockedStatusChanged(network: Network, blocked: Boolean) {
- Log.d(TAG, "onBlockedStatusChanged $network $blocked")
- history.add(BlockedStatus(network, blocked))
- }
-
- // Cannot do:
- // fun onBlockedStatusChanged(network: Network, blocked: Int) {
- // because on S, that needs to be "override fun", and on R, that cannot be "override fun".
- override fun onNetworkSuspended(network: Network) {
- Log.d(TAG, "onNetworkSuspended $network $network")
- history.add(Suspended(network))
- }
-
- override fun onNetworkResumed(network: Network) {
- Log.d(TAG, "$network onNetworkResumed $network")
- history.add(Resumed(network))
- }
-
- override fun onLosing(network: Network, maxMsToLive: Int) {
- Log.d(TAG, "onLosing $network $maxMsToLive")
- history.add(Losing(network, maxMsToLive))
- }
-
- override fun onLost(network: Network) {
- Log.d(TAG, "onLost $network")
- history.add(Lost(network))
- }
-
- override fun onUnavailable() {
- Log.d(TAG, "onUnavailable")
- history.add(Unavailable())
- }
-}
-
-private const val DEFAULT_TIMEOUT = 30_000L // ms
-private const val DEFAULT_NO_CALLBACK_TIMEOUT = 200L // ms
-private val NOOP = Runnable {}
-
-/**
- * See comments on the public constructor below for a description of the arguments.
- */
-open class TestableNetworkCallback private constructor(
- src: TestableNetworkCallback?,
- val defaultTimeoutMs: Long = DEFAULT_TIMEOUT,
- val defaultNoCallbackTimeoutMs: Long = DEFAULT_NO_CALLBACK_TIMEOUT,
- val waiterFunc: Runnable = NOOP // "() -> Unit" would forbid calling with a void func from Java
-) : RecorderCallback(src) {
- /**
- * Construct a testable network callback.
- * @param timeoutMs the default timeout for expecting a callback. Default 30 seconds. This
- * should be long in most cases, because the success case doesn't incur
- * the wait.
- * @param noCallbackTimeoutMs the timeout for expecting that no callback is received. Default
- * 200ms. Because the success case does incur the timeout, this
- * should be short in most cases, but not so short as to frequently
- * time out before an incorrect callback is received.
- * @param waiterFunc a function to use before asserting no callback. For some specific tests,
- * it is useful to run test-specific code before asserting no callback to
- * increase the likelihood that a spurious callback is correctly detected.
- * As an example, a unit test using mock loopers may want to use this to
- * make sure the loopers are drained before asserting no callback, since
- * one of them may cause a callback to be called. @see ConnectivityServiceTest
- * for such an example.
- */
- @JvmOverloads
- constructor(
- timeoutMs: Long = DEFAULT_TIMEOUT,
- noCallbackTimeoutMs: Long = DEFAULT_NO_CALLBACK_TIMEOUT,
- waiterFunc: Runnable = NOOP
- ) : this(null, timeoutMs, noCallbackTimeoutMs, waiterFunc)
-
- fun createLinkedCopy() = TestableNetworkCallback(
- this, defaultTimeoutMs, defaultNoCallbackTimeoutMs, waiterFunc)
-
- // The last available network, or null if any network was lost since the last call to
- // onAvailable. TODO : fix this by fixing the tests that rely on this behavior
- val lastAvailableNetwork: Network?
- get() = when (val it = history.lastOrNull { it is Available || it is Lost }) {
- is Available -> it.network
- else -> null
- }
-
- /**
- * Get the next callback or null if timeout.
- *
- * With no argument, this method waits out the default timeout. To wait forever, pass
- * Long.MAX_VALUE.
- */
- @JvmOverloads
- fun poll(timeoutMs: Long = defaultTimeoutMs, predicate: (CallbackEntry) -> Boolean = { true }) =
- history.poll(timeoutMs, predicate)
-
- /*****
- * expect family of methods.
- * These methods fetch the next callback and assert it matches the conditions : type,
- * passed predicate. If no callback is received within the timeout, these methods fail.
- */
- @JvmOverloads
- fun <T : CallbackEntry> expect(
- type: KClass<T>,
- network: Network = ANY_NETWORK,
- timeoutMs: Long = defaultTimeoutMs,
- errorMsg: String? = null,
- test: (T) -> Boolean = { true }
- ) = expect<CallbackEntry>(network, timeoutMs, errorMsg) {
- if (type.isInstance(it)) {
- test(it as T) // Cast can't fail since type.isInstance(it) and type: KClass<T>
- } else {
- fail("Expected callback ${type.simpleName}, got $it")
- }
- } as T
-
- @JvmOverloads
- fun <T : CallbackEntry> expect(
- type: KClass<T>,
- network: HasNetwork,
- timeoutMs: Long = defaultTimeoutMs,
- errorMsg: String? = null,
- test: (T) -> Boolean = { true }
- ) = expect(type, network.network, timeoutMs, errorMsg, test)
-
- // Java needs an explicit overload to let it omit arguments in the middle, so define these
- // here. Note that @JvmOverloads give us the versions without the last arguments too, so
- // there is no need to explicitly define versions without the test predicate.
- // Without |network|
- @JvmOverloads
- fun <T : CallbackEntry> expect(
- type: KClass<T>,
- timeoutMs: Long,
- errorMsg: String?,
- test: (T) -> Boolean = { true }
- ) = expect(type, ANY_NETWORK, timeoutMs, errorMsg, test)
-
- // Without |timeout|, in Network and HasNetwork versions
- @JvmOverloads
- fun <T : CallbackEntry> expect(
- type: KClass<T>,
- network: Network,
- errorMsg: String?,
- test: (T) -> Boolean = { true }
- ) = expect(type, network, defaultTimeoutMs, errorMsg, test)
-
- @JvmOverloads
- fun <T : CallbackEntry> expect(
- type: KClass<T>,
- network: HasNetwork,
- errorMsg: String?,
- test: (T) -> Boolean = { true }
- ) = expect(type, network.network, defaultTimeoutMs, errorMsg, test)
-
- // Without |errorMsg|, in Network and HasNetwork versions
- @JvmOverloads
- fun <T : CallbackEntry> expect(
- type: KClass<T>,
- network: Network,
- timeoutMs: Long,
- test: (T) -> Boolean
- ) = expect(type, network, timeoutMs, null, test)
-
- @JvmOverloads
- fun <T : CallbackEntry> expect(
- type: KClass<T>,
- network: HasNetwork,
- timeoutMs: Long,
- test: (T) -> Boolean
- ) = expect(type, network.network, timeoutMs, null, test)
-
- // Without |network| or |timeout|
- @JvmOverloads
- fun <T : CallbackEntry> expect(
- type: KClass<T>,
- errorMsg: String?,
- test: (T) -> Boolean = { true }
- ) = expect(type, ANY_NETWORK, defaultTimeoutMs, errorMsg, test)
-
- // Without |network| or |errorMsg|
- @JvmOverloads
- fun <T : CallbackEntry> expect(
- type: KClass<T>,
- timeoutMs: Long,
- test: (T) -> Boolean = { true }
- ) = expect(type, ANY_NETWORK, timeoutMs, null, test)
-
- // Without |timeout| or |errorMsg|, in Network and HasNetwork versions
- @JvmOverloads
- fun <T : CallbackEntry> expect(
- type: KClass<T>,
- network: Network,
- test: (T) -> Boolean
- ) = expect(type, network, defaultTimeoutMs, null, test)
-
- @JvmOverloads
- fun <T : CallbackEntry> expect(
- type: KClass<T>,
- network: HasNetwork,
- test: (T) -> Boolean
- ) = expect(type, network.network, defaultTimeoutMs, null, test)
-
- // Without |network| or |timeout| or |errorMsg|
- @JvmOverloads
- fun <T : CallbackEntry> expect(
- type: KClass<T>,
- test: (T) -> Boolean
- ) = expect(type, ANY_NETWORK, defaultTimeoutMs, null, test)
-
- // Kotlin reified versions. Don't call methods above, or the predicate would need to be noinline
- inline fun <reified T : CallbackEntry> expect(
- network: Network = ANY_NETWORK,
- timeoutMs: Long = defaultTimeoutMs,
- errorMsg: String? = null,
- test: (T) -> Boolean = { true }
- ) = (poll(timeoutMs) ?: fail("Did not receive ${T::class.simpleName} after ${timeoutMs}ms"))
- .also {
- if (it !is T) fail("Expected callback ${T::class.simpleName}, got $it")
- if (ANY_NETWORK !== network && it.network != network) {
- fail("Expected network $network for callback : $it")
- }
- if (!test(it)) {
- fail("${errorMsg ?: "Callback doesn't match predicate"} : $it")
- }
- } as T
-
- inline fun <reified T : CallbackEntry> expect(
- network: HasNetwork,
- timeoutMs: Long = defaultTimeoutMs,
- errorMsg: String? = null,
- test: (T) -> Boolean = { true }
- ) = expect(network.network, timeoutMs, errorMsg, test)
-
- /*****
- * assertNoCallback family of methods.
- * These methods make sure that no callback that matches the predicate was received.
- * If no predicate is given, they make sure that no callback at all was received.
- * These methods run the waiter func given in the constructor if any.
- */
- @JvmOverloads
- fun assertNoCallback(
- timeoutMs: Long = defaultNoCallbackTimeoutMs,
- valid: (CallbackEntry) -> Boolean = { true }
- ) {
- waiterFunc.run()
- history.poll(timeoutMs) { valid(it) }?.let { fail("Expected no callback but got $it") }
- }
-
- fun assertNoCallback(valid: (CallbackEntry) -> Boolean) =
- assertNoCallback(defaultNoCallbackTimeoutMs, valid)
-
- /*****
- * eventuallyExpect family of methods.
- * These methods make sure a callback that matches the type/predicate is received eventually.
- * Any callback of the wrong type, or doesn't match the optional predicate, is ignored.
- * They fail if no callback matching the predicate is received within the timeout.
- */
- inline fun <reified T : CallbackEntry> eventuallyExpect(
- timeoutMs: Long = defaultTimeoutMs,
- from: Int = mark,
- crossinline predicate: (T) -> Boolean = { true }
- ): T = history.poll(timeoutMs, from) { it is T && predicate(it) }.also {
- assertNotNull(it, "Callback ${T::class} not received within ${timeoutMs}ms. " +
- "Got ${history.backtrace()}")
- } as T
-
- @JvmOverloads
- fun <T : CallbackEntry> eventuallyExpect(
- type: KClass<T>,
- timeoutMs: Long = defaultTimeoutMs,
- predicate: (cb: T) -> Boolean = { true }
- ) = history.poll(timeoutMs) { type.java.isInstance(it) && predicate(it as T) }.also {
- assertNotNull(it, "Callback ${type.java} not received within ${timeoutMs}ms. " +
- "Got ${history.backtrace()}")
- } as T
-
- fun <T : CallbackEntry> eventuallyExpect(
- type: KClass<T>,
- timeoutMs: Long = defaultTimeoutMs,
- from: Int = mark,
- predicate: (cb: T) -> Boolean = { true }
- ) = history.poll(timeoutMs, from) { type.java.isInstance(it) && predicate(it as T) }.also {
- assertNotNull(it, "Callback ${type.java} not received within ${timeoutMs}ms. " +
- "Got ${history.backtrace()}")
- } as T
-
- // Expects onAvailable and the callbacks that follow it. These are:
- // - onSuspended, iff the network was suspended when the callbacks fire.
- // - onCapabilitiesChanged.
- // - onLinkPropertiesChanged.
- // - onBlockedStatusChanged.
- //
- // @param network the network to expect the callbacks on.
- // @param suspended whether to expect a SUSPENDED callback.
- // @param validated the expected value of the VALIDATED capability in the
- // onCapabilitiesChanged callback.
- // @param tmt how long to wait for the callbacks.
- @JvmOverloads
- fun expectAvailableCallbacks(
- net: Network,
- suspended: Boolean = false,
- validated: Boolean? = true,
- blocked: Boolean = false,
- tmt: Long = defaultTimeoutMs
- ) {
- expectAvailableCallbacksCommon(net, suspended, validated, tmt)
- expect<BlockedStatus>(net, tmt) { it.blocked == blocked }
- }
-
- fun expectAvailableCallbacks(
- net: Network,
- suspended: Boolean,
- validated: Boolean,
- blockedReason: Int,
- tmt: Long
- ) {
- expectAvailableCallbacksCommon(net, suspended, validated, tmt)
- expect<BlockedStatusInt>(net) { it.reason == blockedReason }
- }
-
- private fun expectAvailableCallbacksCommon(
- net: Network,
- suspended: Boolean,
- validated: Boolean?,
- tmt: Long
- ) {
- expect<Available>(net, tmt)
- if (suspended) {
- expect<Suspended>(net, tmt)
- }
- expect<CapabilitiesChanged>(net, tmt) {
- validated == null || validated == it.caps.hasCapability(NET_CAPABILITY_VALIDATED)
- }
- expect<LinkPropertiesChanged>(net, tmt)
- }
-
- // Backward compatibility for existing Java code. Use named arguments instead and remove all
- // these when there is no user left.
- fun expectAvailableAndSuspendedCallbacks(
- net: Network,
- validated: Boolean,
- tmt: Long = defaultTimeoutMs
- ) = expectAvailableCallbacks(net, suspended = true, validated = validated, tmt = tmt)
-
- // Expects the available callbacks (where the onCapabilitiesChanged must contain the
- // VALIDATED capability), plus another onCapabilitiesChanged which is identical to the
- // one we just sent.
- // TODO: this is likely a bug. Fix it and remove this method.
- fun expectAvailableDoubleValidatedCallbacks(net: Network, tmt: Long = defaultTimeoutMs) {
- val mark = history.mark
- expectAvailableCallbacks(net, tmt = tmt)
- val firstCaps = history.poll(tmt, mark) { it is CapabilitiesChanged }
- assertEquals(firstCaps, expect<CapabilitiesChanged>(net, tmt))
- }
-
- // Expects the available callbacks where the onCapabilitiesChanged must not have validated,
- // then expects another onCapabilitiesChanged that has the validated bit set. This is used
- // when a network connects and satisfies a callback, and then immediately validates.
- fun expectAvailableThenValidatedCallbacks(net: Network, tmt: Long = defaultTimeoutMs) {
- expectAvailableCallbacks(net, validated = false, tmt = tmt)
- expectCaps(net, tmt) { it.hasCapability(NET_CAPABILITY_VALIDATED) }
- }
-
- fun expectAvailableThenValidatedCallbacks(
- net: Network,
- blockedReason: Int,
- tmt: Long = defaultTimeoutMs
- ) {
- expectAvailableCallbacks(net, validated = false, suspended = false,
- blockedReason = blockedReason, tmt = tmt)
- expectCaps(net, tmt) { it.hasCapability(NET_CAPABILITY_VALIDATED) }
- }
-
- // Temporary Java compat measure : have MockNetworkAgent implement this so that all existing
- // calls with networkAgent can be routed through here without moving MockNetworkAgent.
- // TODO: clean this up, remove this method.
- interface HasNetwork {
- val network: Network
- }
-
- fun expectAvailableCallbacks(
- n: HasNetwork,
- suspended: Boolean,
- validated: Boolean,
- blocked: Boolean,
- timeoutMs: Long
- ) = expectAvailableCallbacks(n.network, suspended, validated, blocked, timeoutMs)
-
- fun expectAvailableAndSuspendedCallbacks(n: HasNetwork, expectValidated: Boolean) {
- expectAvailableAndSuspendedCallbacks(n.network, expectValidated)
- }
-
- fun expectAvailableCallbacksValidated(n: HasNetwork) {
- expectAvailableCallbacks(n.network)
- }
-
- fun expectAvailableCallbacksValidatedAndBlocked(n: HasNetwork) {
- expectAvailableCallbacks(n.network, blocked = true)
- }
-
- fun expectAvailableCallbacksUnvalidated(n: HasNetwork) {
- expectAvailableCallbacks(n.network, validated = false)
- }
-
- fun expectAvailableCallbacksUnvalidatedAndBlocked(n: HasNetwork) {
- expectAvailableCallbacks(n.network, validated = false, blocked = true)
- }
-
- fun expectAvailableDoubleValidatedCallbacks(n: HasNetwork) {
- expectAvailableDoubleValidatedCallbacks(n.network, defaultTimeoutMs)
- }
-
- fun expectAvailableThenValidatedCallbacks(n: HasNetwork) {
- expectAvailableThenValidatedCallbacks(n.network, defaultTimeoutMs)
- }
-
- @JvmOverloads
- fun expectCaps(
- n: HasNetwork,
- tmt: Long = defaultTimeoutMs,
- valid: (NetworkCapabilities) -> Boolean = { true }
- ) = expect<CapabilitiesChanged>(n.network, tmt) { valid(it.caps) }.caps
-
- @JvmOverloads
- fun expectCaps(
- n: Network,
- tmt: Long = defaultTimeoutMs,
- valid: (NetworkCapabilities) -> Boolean
- ) = expect<CapabilitiesChanged>(n, tmt) { valid(it.caps) }.caps
-
- fun expectCaps(
- n: HasNetwork,
- valid: (NetworkCapabilities) -> Boolean
- ) = expect<CapabilitiesChanged>(n.network) { valid(it.caps) }.caps
-
- fun expectCaps(
- tmt: Long,
- valid: (NetworkCapabilities) -> Boolean
- ) = expect<CapabilitiesChanged>(ANY_NETWORK, tmt) { valid(it.caps) }.caps
-}
diff --git a/common/testutils/devicetests/com/android/testutils/TestableNetworkOfferCallback.kt b/common/testutils/devicetests/com/android/testutils/TestableNetworkOfferCallback.kt
deleted file mode 100644
index 21bd60c9..00000000
--- a/common/testutils/devicetests/com/android/testutils/TestableNetworkOfferCallback.kt
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2021 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.net.NetworkCapabilities
-import android.net.NetworkProvider
-import android.net.NetworkRequest
-import android.util.Log
-import com.android.net.module.util.ArrayTrackRecord
-import kotlin.test.fail
-
-class TestableNetworkOfferCallback(val timeoutMs: Long, private val noCallbackTimeoutMs: Long)
- : NetworkProvider.NetworkOfferCallback {
- private val TAG = this::class.simpleName
- val history = ArrayTrackRecord<CallbackEntry>().newReadHead()
-
- sealed class CallbackEntry {
- data class OnNetworkNeeded(val request: NetworkRequest) : CallbackEntry()
- data class OnNetworkUnneeded(val request: NetworkRequest) : CallbackEntry()
- }
-
- /**
- * Called by the system when a network for this offer is needed to satisfy some
- * networking request.
- */
- override fun onNetworkNeeded(request: NetworkRequest) {
- Log.d(TAG, "onNetworkNeeded $request")
- history.add(CallbackEntry.OnNetworkNeeded(request))
- }
-
- /**
- * Called by the system when this offer is no longer valuable for this request.
- */
- override fun onNetworkUnneeded(request: NetworkRequest) {
- Log.d(TAG, "onNetworkUnneeded $request")
- history.add(CallbackEntry.OnNetworkUnneeded(request))
- }
-
- inline fun <reified T : CallbackEntry> expectCallbackThat(
- crossinline predicate: (T) -> Boolean
- ) {
- val event = history.poll(timeoutMs)
- ?: fail("Did not receive callback after ${timeoutMs}ms")
- if (event !is T || !predicate(event)) fail("Received unexpected callback $event")
- }
-
- fun expectOnNetworkNeeded(capabilities: NetworkCapabilities) =
- expectCallbackThat<CallbackEntry.OnNetworkNeeded> {
- it.request.canBeSatisfiedBy(capabilities)
- }
-
- fun expectOnNetworkUnneeded(capabilities: NetworkCapabilities) =
- expectCallbackThat<CallbackEntry.OnNetworkUnneeded> {
- it.request.canBeSatisfiedBy(capabilities)
- }
-
- fun assertNoCallback() {
- val cb = history.poll(noCallbackTimeoutMs)
- if (null != cb) fail("Expected no callback but got $cb")
- }
-} \ No newline at end of file
diff --git a/common/testutils/devicetests/com/android/testutils/TestableNetworkStatsProvider.kt b/common/testutils/devicetests/com/android/testutils/TestableNetworkStatsProvider.kt
deleted file mode 100644
index 4a7b3513..00000000
--- a/common/testutils/devicetests/com/android/testutils/TestableNetworkStatsProvider.kt
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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.net.netstats.provider.NetworkStatsProvider
-import android.util.Log
-import com.android.net.module.util.ArrayTrackRecord
-import kotlin.test.assertEquals
-import kotlin.test.assertTrue
-import kotlin.test.fail
-
-private const val DEFAULT_TIMEOUT_MS = 200L
-const val TOKEN_ANY = -1
-
-open class TestableNetworkStatsProvider(
- val defaultTimeoutMs: Long = DEFAULT_TIMEOUT_MS
-) : NetworkStatsProvider() {
- sealed class CallbackType {
- data class OnRequestStatsUpdate(val token: Int) : CallbackType()
- data class OnSetWarningAndLimit(
- val iface: String,
- val warningBytes: Long,
- val limitBytes: Long
- ) : CallbackType()
- data class OnSetLimit(val iface: String, val limitBytes: Long) : CallbackType() {
- // Add getter for backward compatibility since old tests do not recognize limitBytes.
- val quotaBytes: Long
- get() = limitBytes
- }
- data class OnSetAlert(val quotaBytes: Long) : CallbackType()
- }
-
- private val TAG = this::class.simpleName
- val history = ArrayTrackRecord<CallbackType>().newReadHead()
- // See ReadHead#mark
- val mark get() = history.mark
-
- override fun onRequestStatsUpdate(token: Int) {
- Log.d(TAG, "onRequestStatsUpdate $token")
- history.add(CallbackType.OnRequestStatsUpdate(token))
- }
-
- override fun onSetWarningAndLimit(iface: String, warningBytes: Long, limitBytes: Long) {
- Log.d(TAG, "onSetWarningAndLimit $iface $warningBytes $limitBytes")
- history.add(CallbackType.OnSetWarningAndLimit(iface, warningBytes, limitBytes))
- }
-
- override fun onSetLimit(iface: String, quotaBytes: Long) {
- Log.d(TAG, "onSetLimit $iface $quotaBytes")
- history.add(CallbackType.OnSetLimit(iface, quotaBytes))
- }
-
- override fun onSetAlert(quotaBytes: Long) {
- Log.d(TAG, "onSetAlert $quotaBytes")
- history.add(CallbackType.OnSetAlert(quotaBytes))
- }
-
- fun expectOnRequestStatsUpdate(token: Int, timeout: Long = defaultTimeoutMs): Int {
- val event = history.poll(timeout)
- assertTrue(event is CallbackType.OnRequestStatsUpdate)
- if (token != TOKEN_ANY) {
- assertEquals(token, event.token)
- }
- return event.token
- }
-
- fun expectOnSetLimit(iface: String, quotaBytes: Long, timeout: Long = defaultTimeoutMs) {
- assertEquals(CallbackType.OnSetLimit(iface, quotaBytes), history.poll(timeout))
- }
-
- fun expectOnSetAlert(quotaBytes: Long, timeout: Long = defaultTimeoutMs) {
- assertEquals(CallbackType.OnSetAlert(quotaBytes), history.poll(timeout))
- }
-
- fun pollForNextCallback(timeout: Long = defaultTimeoutMs) =
- history.poll(timeout) ?: fail("Did not receive callback after ${timeout}ms")
-
- inline fun <reified T : CallbackType> expectCallback(
- timeout: Long = defaultTimeoutMs,
- predicate: (T) -> Boolean = { true }
- ): T {
- return pollForNextCallback(timeout).also { assertTrue(it is T && predicate(it)) } as T
- }
-
- // Expects a callback of the specified type matching the predicate within the timeout.
- // Any callback that doesn't match the predicate will be skipped. Fails only if
- // no matching callback is received within the timeout.
- // TODO : factorize the code for this with the identical call in TestableNetworkCallback.
- // There should be a common superclass doing this generically.
- // TODO : have a better error message to have this fail. Right now the failure when no
- // matching callback arrives comes from the casting to a non-nullable T.
- // TODO : in fact, completely removing this method and have clients use
- // history.poll(timeout, index, predicate) directly might be simpler.
- inline fun <reified T : CallbackType> eventuallyExpect(
- timeoutMs: Long = defaultTimeoutMs,
- from: Int = mark,
- crossinline predicate: (T) -> Boolean = { true }
- ) = history.poll(timeoutMs, from) { it is T && predicate(it) } as T
-
- fun drainCallbacks() {
- history.mark = history.size
- }
-
- @JvmOverloads
- fun assertNoCallback(timeout: Long = defaultTimeoutMs) {
- val cb = history.poll(timeout)
- cb?.let { fail("Expected no callback but got $cb") }
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/TestableNetworkStatsProviderBinder.kt b/common/testutils/devicetests/com/android/testutils/TestableNetworkStatsProviderBinder.kt
deleted file mode 100644
index 643346b2..00000000
--- a/common/testutils/devicetests/com/android/testutils/TestableNetworkStatsProviderBinder.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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 com.android.net.module.util.ArrayTrackRecord
-import kotlin.test.assertEquals
-import kotlin.test.fail
-
-private const val DEFAULT_TIMEOUT_MS = 200L
-
-open class TestableNetworkStatsProviderBinder : NetworkStatsProviderStubCompat() {
- sealed class CallbackType {
- data class OnRequestStatsUpdate(val token: Int) : CallbackType()
- data class OnSetAlert(val quotaBytes: Long) : CallbackType()
- data class OnSetWarningAndLimit(
- val iface: String,
- val warningBytes: Long,
- val limitBytes: Long
- ) : CallbackType()
- }
-
- private val history = ArrayTrackRecord<CallbackType>().ReadHead()
-
- override fun onRequestStatsUpdate(token: Int) {
- history.add(CallbackType.OnRequestStatsUpdate(token))
- }
-
- override fun onSetAlert(quotaBytes: Long) {
- history.add(CallbackType.OnSetAlert(quotaBytes))
- }
-
- override fun onSetWarningAndLimit(iface: String, warningBytes: Long, limitBytes: Long) {
- history.add(CallbackType.OnSetWarningAndLimit(iface, warningBytes, limitBytes))
- }
-
- fun expectOnRequestStatsUpdate(token: Int) {
- assertEquals(CallbackType.OnRequestStatsUpdate(token), history.poll(DEFAULT_TIMEOUT_MS))
- }
-
- fun expectOnSetWarningAndLimit(iface: String, warningBytes: Long, limitBytes: Long) {
- assertEquals(CallbackType.OnSetWarningAndLimit(iface, warningBytes, limitBytes),
- history.poll(DEFAULT_TIMEOUT_MS))
- }
-
- fun expectOnSetAlert(quotaBytes: Long) {
- assertEquals(CallbackType.OnSetAlert(quotaBytes), history.poll(DEFAULT_TIMEOUT_MS))
- }
-
- @JvmOverloads
- fun assertNoCallback(timeout: Long = DEFAULT_TIMEOUT_MS) {
- val cb = history.poll(timeout)
- cb?.let { fail("Expected no callback but got $cb") }
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/TestableNetworkStatsProviderCbBinder.kt b/common/testutils/devicetests/com/android/testutils/TestableNetworkStatsProviderCbBinder.kt
deleted file mode 100644
index 5547c90a..00000000
--- a/common/testutils/devicetests/com/android/testutils/TestableNetworkStatsProviderCbBinder.kt
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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.net.NetworkStats
-import com.android.net.module.util.ArrayTrackRecord
-import kotlin.test.assertEquals
-import kotlin.test.assertTrue
-import kotlin.test.fail
-
-private const val DEFAULT_TIMEOUT_MS = 3000L
-
-open class TestableNetworkStatsProviderCbBinder : NetworkStatsProviderCbStubCompat() {
- sealed class CallbackType {
- data class NotifyStatsUpdated(
- val token: Int,
- val ifaceStats: NetworkStats,
- val uidStats: NetworkStats
- ) : CallbackType()
- object NotifyWarningReached : CallbackType()
- object NotifyLimitReached : CallbackType()
- object NotifyWarningOrLimitReached : CallbackType()
- object NotifyAlertReached : CallbackType()
- object Unregister : CallbackType()
- }
-
- private val history = ArrayTrackRecord<CallbackType>().ReadHead()
-
- override fun notifyStatsUpdated(token: Int, ifaceStats: NetworkStats, uidStats: NetworkStats) {
- history.add(CallbackType.NotifyStatsUpdated(token, ifaceStats, uidStats))
- }
-
- override fun notifyWarningReached() {
- history.add(CallbackType.NotifyWarningReached)
- }
-
- override fun notifyLimitReached() {
- history.add(CallbackType.NotifyLimitReached)
- }
-
- override fun notifyWarningOrLimitReached() {
- // Older callback is split into notifyLimitReached and notifyWarningReached in T.
- history.add(CallbackType.NotifyWarningOrLimitReached)
- }
-
- override fun notifyAlertReached() {
- history.add(CallbackType.NotifyAlertReached)
- }
-
- override fun unregister() {
- history.add(CallbackType.Unregister)
- }
-
- fun expectNotifyStatsUpdated() {
- val event = history.poll(DEFAULT_TIMEOUT_MS)
- assertTrue(event is CallbackType.NotifyStatsUpdated)
- }
-
- fun expectNotifyStatsUpdated(ifaceStats: NetworkStats, uidStats: NetworkStats) {
- val event = history.poll(DEFAULT_TIMEOUT_MS)!!
- if (event !is CallbackType.NotifyStatsUpdated) {
- throw Exception("Expected NotifyStatsUpdated callback, but got ${event::class}")
- }
- // TODO: verify token.
- assertNetworkStatsEquals(ifaceStats, event.ifaceStats)
- assertNetworkStatsEquals(uidStats, event.uidStats)
- }
-
- fun expectNotifyWarningReached() =
- assertEquals(CallbackType.NotifyWarningReached, history.poll(DEFAULT_TIMEOUT_MS))
-
- fun expectNotifyLimitReached() =
- assertEquals(CallbackType.NotifyLimitReached, history.poll(DEFAULT_TIMEOUT_MS))
-
- fun expectNotifyWarningOrLimitReached() =
- assertEquals(CallbackType.NotifyWarningOrLimitReached, history.poll(DEFAULT_TIMEOUT_MS))
-
- fun expectNotifyAlertReached() =
- assertEquals(CallbackType.NotifyAlertReached, history.poll(DEFAULT_TIMEOUT_MS))
-
- // Assert there is no callback in current queue.
- fun assertNoCallback() {
- val cb = history.poll(0)
- cb?.let { fail("Expected no callback but got $cb") }
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/async/FakeOsAccess.java b/common/testutils/devicetests/com/android/testutils/async/FakeOsAccess.java
deleted file mode 100644
index 48b57d70..00000000
--- a/common/testutils/devicetests/com/android/testutils/async/FakeOsAccess.java
+++ /dev/null
@@ -1,568 +0,0 @@
-/*
- * Copyright (C) 2023 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.async;
-
-import android.os.ParcelFileDescriptor;
-import android.system.StructPollfd;
-import android.util.Log;
-
-import com.android.net.module.util.async.CircularByteBuffer;
-import com.android.net.module.util.async.OsAccess;
-
-import java.io.FileDescriptor;
-import java.io.InterruptedIOException;
-import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.lang.reflect.Field;
-import java.util.HashMap;
-import java.util.concurrent.TimeUnit;
-
-public class FakeOsAccess extends OsAccess {
- public static final boolean ENABLE_FINE_DEBUG = true;
-
- public static final int DEFAULT_FILE_DATA_QUEUE_SIZE = 8 * 1024;
-
- private enum FileType { PAIR, PIPE }
-
- // Common poll() constants:
- private static final short POLLIN = 0x0001;
- private static final short POLLOUT = 0x0004;
- private static final short POLLERR = 0x0008;
- private static final short POLLHUP = 0x0010;
-
- private static final Constructor<FileDescriptor> FD_CONSTRUCTOR;
- private static final Field FD_FIELD_DESCRIPTOR;
- private static final Field PFD_FIELD_DESCRIPTOR;
- private static final Field PFD_FIELD_GUARD;
- private static final Method CLOSE_GUARD_METHOD_CLOSE;
-
- private final int mReadQueueSize = DEFAULT_FILE_DATA_QUEUE_SIZE;
- private final int mWriteQueueSize = DEFAULT_FILE_DATA_QUEUE_SIZE;
- private final HashMap<Integer, File> mFiles = new HashMap<>();
- private final byte[] mTmpBuffer = new byte[1024];
- private final long mStartTime;
- private final String mLogTag;
- private int mFileNumberGen = 3;
- private boolean mHasRateLimitedData;
-
- public FakeOsAccess(String logTag) {
- mLogTag = logTag;
- mStartTime = monotonicTimeMillis();
- }
-
- @Override
- public long monotonicTimeMillis() {
- return System.nanoTime() / 1000000;
- }
-
- @Override
- public FileDescriptor getInnerFileDescriptor(ParcelFileDescriptor fd) {
- try {
- return (FileDescriptor) PFD_FIELD_DESCRIPTOR.get(fd);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public void close(ParcelFileDescriptor fd) {
- if (fd != null) {
- close(getInnerFileDescriptor(fd));
-
- try {
- // Reduce CloseGuard warnings.
- Object guard = PFD_FIELD_GUARD.get(fd);
- CLOSE_GUARD_METHOD_CLOSE.invoke(guard);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- public synchronized void close(FileDescriptor fd) {
- if (fd != null) {
- File file = getFileOrNull(fd);
- if (file != null) {
- file.decreaseRefCount();
- mFiles.remove(getFileDescriptorNumber(fd));
- setFileDescriptorNumber(fd, -1);
- notifyAll();
- }
- }
- }
-
- private File getFile(String func, FileDescriptor fd) throws IOException {
- File file = getFileOrNull(fd);
- if (file == null) {
- throw newIOException(func, "Unknown file descriptor: " + getFileDebugName(fd));
- }
- return file;
- }
-
- private File getFileOrNull(FileDescriptor fd) {
- return mFiles.get(getFileDescriptorNumber(fd));
- }
-
- @Override
- public String getFileDebugName(ParcelFileDescriptor fd) {
- return (fd != null ? getFileDebugName(getInnerFileDescriptor(fd)) : "null");
- }
-
- public String getFileDebugName(FileDescriptor fd) {
- if (fd == null) {
- return "null";
- }
-
- final int fdNumber = getFileDescriptorNumber(fd);
- File file = mFiles.get(fdNumber);
-
- StringBuilder sb = new StringBuilder();
- if (file != null) {
- if (file.name != null) {
- sb.append(file.name);
- sb.append("/");
- }
- sb.append(file.type);
- sb.append("/");
- } else {
- sb.append("BADFD/");
- }
- sb.append(fdNumber);
- return sb.toString();
- }
-
- public synchronized void setFileName(FileDescriptor fd, String name) {
- File file = getFileOrNull(fd);
- if (file != null) {
- file.name = name;
- }
- }
-
- @Override
- public synchronized void setNonBlocking(FileDescriptor fd) throws IOException {
- File file = getFile("fcntl", fd);
- file.isBlocking = false;
- }
-
- @Override
- public synchronized int read(FileDescriptor fd, byte[] buffer, int pos, int len)
- throws IOException {
- checkBoundaries("read", buffer, pos, len);
-
- File file = getFile("read", fd);
- if (file.readQueue == null) {
- throw newIOException("read", "File not readable");
- }
- file.checkNonBlocking("read");
-
- if (len == 0) {
- return 0;
- }
-
- final int availSize = file.readQueue.size();
- if (availSize == 0) {
- if (file.isEndOfStream) {
- // Java convention uses -1 to indicate end of stream.
- return -1;
- }
- return 0; // EAGAIN
- }
-
- final int readCount = Math.min(len, availSize);
- file.readQueue.readBytes(buffer, pos, readCount);
- maybeTransferData(file);
- return readCount;
- }
-
- @Override
- public synchronized int write(FileDescriptor fd, byte[] buffer, int pos, int len)
- throws IOException {
- checkBoundaries("write", buffer, pos, len);
-
- File file = getFile("write", fd);
- if (file.writeQueue == null) {
- throw newIOException("read", "File not writable");
- }
- if (file.type == FileType.PIPE && file.sink.openCount == 0) {
- throw newIOException("write", "The other end of pipe is closed");
- }
- file.checkNonBlocking("write");
-
- if (len == 0) {
- return 0;
- }
-
- final int originalFreeSize = file.writeQueue.freeSize();
- if (originalFreeSize == 0) {
- return 0; // EAGAIN
- }
-
- final int writeCount = Math.min(len, originalFreeSize);
- file.writeQueue.writeBytes(buffer, pos, writeCount);
- maybeTransferData(file);
-
- if (file.writeQueue.freeSize() < originalFreeSize) {
- final int additionalQueuedCount = originalFreeSize - file.writeQueue.freeSize();
- Log.i(mLogTag, logStr("Delaying transfer of " + additionalQueuedCount
- + " bytes, queued=" + file.writeQueue.size() + ", type=" + file.type
- + ", src_red=" + file.outboundLimiter + ", dst_red=" + file.sink.inboundLimiter));
- }
-
- return writeCount;
- }
-
- private void maybeTransferData(File file) {
- boolean hasChanges = copyFileBuffers(file, file.sink);
- hasChanges = copyFileBuffers(file.source, file) || hasChanges;
-
- if (hasChanges) {
- // TODO(b/245971639): Avoid notifying if no-one is polling.
- notifyAll();
- }
- }
-
- private boolean copyFileBuffers(File src, File dst) {
- if (src.writeQueue == null || dst.readQueue == null) {
- return false;
- }
-
- final int originalCopyCount = Math.min(mTmpBuffer.length,
- Math.min(src.writeQueue.size(), dst.readQueue.freeSize()));
-
- final int allowedCopyCount = RateLimiter.limit(
- src.outboundLimiter, dst.inboundLimiter, originalCopyCount);
-
- if (allowedCopyCount < originalCopyCount) {
- if (ENABLE_FINE_DEBUG) {
- Log.i(mLogTag, logStr("Delaying transfer of "
- + (originalCopyCount - allowedCopyCount) + " bytes, original="
- + originalCopyCount + ", allowed=" + allowedCopyCount
- + ", type=" + src.type));
- }
- if (originalCopyCount > 0) {
- mHasRateLimitedData = true;
- }
- if (allowedCopyCount == 0) {
- return false;
- }
- }
-
- boolean hasChanges = false;
- if (allowedCopyCount > 0) {
- if (dst.readQueue.size() == 0 || src.writeQueue.freeSize() == 0) {
- hasChanges = true; // Read queue had no data, or write queue was full.
- }
- src.writeQueue.readBytes(mTmpBuffer, 0, allowedCopyCount);
- dst.readQueue.writeBytes(mTmpBuffer, 0, allowedCopyCount);
- }
-
- if (!dst.isEndOfStream && src.openCount == 0
- && src.writeQueue.size() == 0 && dst.readQueue.size() == 0) {
- dst.isEndOfStream = true;
- hasChanges = true;
- }
-
- return hasChanges;
- }
-
- public void clearInboundRateLimit(FileDescriptor fd) {
- setInboundRateLimit(fd, Integer.MAX_VALUE);
- }
-
- public void clearOutboundRateLimit(FileDescriptor fd) {
- setOutboundRateLimit(fd, Integer.MAX_VALUE);
- }
-
- public synchronized void setInboundRateLimit(FileDescriptor fd, int bytesPerSecond) {
- File file = getFileOrNull(fd);
- if (file != null) {
- file.inboundLimiter.setBytesPerSecond(bytesPerSecond);
- maybeTransferData(file);
- }
- }
-
- public synchronized void setOutboundRateLimit(FileDescriptor fd, int bytesPerSecond) {
- File file = getFileOrNull(fd);
- if (file != null) {
- file.outboundLimiter.setBytesPerSecond(bytesPerSecond);
- maybeTransferData(file);
- }
- }
-
- public synchronized ParcelFileDescriptor[] socketpair() throws IOException {
- int fdNumber1 = getNextFd("socketpair");
- int fdNumber2 = getNextFd("socketpair");
-
- File file1 = new File(FileType.PAIR, mReadQueueSize, mWriteQueueSize);
- File file2 = new File(FileType.PAIR, mReadQueueSize, mWriteQueueSize);
-
- return registerFilePair(fdNumber1, file1, fdNumber2, file2);
- }
-
- @Override
- public synchronized ParcelFileDescriptor[] pipe() throws IOException {
- int fdNumber1 = getNextFd("pipe");
- int fdNumber2 = getNextFd("pipe");
-
- File file1 = new File(FileType.PIPE, mReadQueueSize, 0);
- File file2 = new File(FileType.PIPE, 0, mWriteQueueSize);
-
- return registerFilePair(fdNumber1, file1, fdNumber2, file2);
- }
-
- private ParcelFileDescriptor[] registerFilePair(
- int fdNumber1, File file1, int fdNumber2, File file2) {
- file1.sink = file2;
- file1.source = file2;
- file2.sink = file1;
- file2.source = file1;
-
- mFiles.put(fdNumber1, file1);
- mFiles.put(fdNumber2, file2);
- return new ParcelFileDescriptor[] {
- newParcelFileDescriptor(fdNumber1), newParcelFileDescriptor(fdNumber2)};
- }
-
- @Override
- public short getPollInMask() {
- return POLLIN;
- }
-
- @Override
- public short getPollOutMask() {
- return POLLOUT;
- }
-
- @Override
- public synchronized int poll(StructPollfd[] fds, int timeoutMs) throws IOException {
- if (timeoutMs < 0) {
- timeoutMs = (int) TimeUnit.HOURS.toMillis(1); // Make "infinite" equal to 1 hour.
- }
-
- if (fds == null || fds.length > 1000) {
- throw newIOException("poll", "Invalid fds param");
- }
- for (StructPollfd pollFd : fds) {
- getFile("poll", pollFd.fd);
- }
-
- int waitCallCount = 0;
- final long deadline = monotonicTimeMillis() + timeoutMs;
- while (true) {
- if (mHasRateLimitedData) {
- mHasRateLimitedData = false;
- for (File file : mFiles.values()) {
- if (file.inboundLimiter.getLastRequestReduction() != 0) {
- copyFileBuffers(file.source, file);
- }
- if (file.outboundLimiter.getLastRequestReduction() != 0) {
- copyFileBuffers(file, file.sink);
- }
- }
- }
-
- final int readyCount = calculateReadyCount(fds);
- if (readyCount > 0) {
- if (ENABLE_FINE_DEBUG) {
- Log.v(mLogTag, logStr("Poll returns " + readyCount
- + " after " + waitCallCount + " wait calls"));
- }
- return readyCount;
- }
-
- long remainingTimeoutMs = deadline - monotonicTimeMillis();
- if (remainingTimeoutMs <= 0) {
- if (ENABLE_FINE_DEBUG) {
- Log.v(mLogTag, logStr("Poll timeout " + timeoutMs
- + "ms after " + waitCallCount + " wait calls"));
- }
- return 0;
- }
-
- if (mHasRateLimitedData) {
- remainingTimeoutMs = Math.min(RateLimiter.BUCKET_DURATION_MS, remainingTimeoutMs);
- }
-
- try {
- wait(remainingTimeoutMs);
- } catch (InterruptedException e) {
- // Ignore and retry
- }
- waitCallCount++;
- }
- }
-
- private int calculateReadyCount(StructPollfd[] fds) {
- int fdCount = 0;
- for (StructPollfd pollFd : fds) {
- pollFd.revents = 0;
-
- File file = getFileOrNull(pollFd.fd);
- if (file == null) {
- Log.w(mLogTag, logStr("Ignoring FD concurrently closed by a buggy app: "
- + getFileDebugName(pollFd.fd)));
- continue;
- }
-
- if (ENABLE_FINE_DEBUG) {
- Log.v(mLogTag, logStr("calculateReadyCount fd=" + getFileDebugName(pollFd.fd)
- + ", events=" + pollFd.events + ", eof=" + file.isEndOfStream
- + ", r=" + (file.readQueue != null ? file.readQueue.size() : -1)
- + ", w=" + (file.writeQueue != null ? file.writeQueue.freeSize() : -1)));
- }
-
- if ((pollFd.events & POLLIN) != 0) {
- if (file.readQueue != null && file.readQueue.size() != 0) {
- pollFd.revents |= POLLIN;
- }
- if (file.isEndOfStream) {
- pollFd.revents |= POLLHUP;
- }
- }
-
- if ((pollFd.events & POLLOUT) != 0) {
- if (file.type == FileType.PIPE && file.sink.openCount == 0) {
- pollFd.revents |= POLLERR;
- }
- if (file.writeQueue != null && file.writeQueue.freeSize() != 0) {
- pollFd.revents |= POLLOUT;
- }
- }
-
- if (pollFd.revents != 0) {
- fdCount++;
- }
- }
- return fdCount;
- }
-
- private int getNextFd(String func) throws IOException {
- if (mFileNumberGen > 100000) {
- throw newIOException(func, "Too many files open");
- }
-
- return mFileNumberGen++;
- }
-
- private static IOException newIOException(String func, String message) {
- return new IOException(message + ", func=" + func);
- }
-
- public static void checkBoundaries(String func, byte[] buffer, int pos, int len)
- throws IOException {
- if (((buffer.length | pos | len) < 0 || pos > buffer.length - len)) {
- throw newIOException(func, "Invalid array bounds");
- }
- }
-
- private ParcelFileDescriptor newParcelFileDescriptor(int fdNumber) {
- try {
- return new ParcelFileDescriptor(newFileDescriptor(fdNumber));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- private FileDescriptor newFileDescriptor(int fdNumber) {
- try {
- return FD_CONSTRUCTOR.newInstance(Integer.valueOf(fdNumber));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- public int getFileDescriptorNumber(FileDescriptor fd) {
- try {
- return (Integer) FD_FIELD_DESCRIPTOR.get(fd);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- private void setFileDescriptorNumber(FileDescriptor fd, int fdNumber) {
- try {
- FD_FIELD_DESCRIPTOR.set(fd, Integer.valueOf(fdNumber));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- private String logStr(String message) {
- return "[FakeOs " + (monotonicTimeMillis() - mStartTime) + "] " + message;
- }
-
- private class File {
- final FileType type;
- final CircularByteBuffer readQueue;
- final CircularByteBuffer writeQueue;
- final RateLimiter inboundLimiter = new RateLimiter(FakeOsAccess.this, Integer.MAX_VALUE);
- final RateLimiter outboundLimiter = new RateLimiter(FakeOsAccess.this, Integer.MAX_VALUE);
- String name;
- int openCount = 1;
- boolean isBlocking = true;
- File sink;
- File source;
- boolean isEndOfStream;
-
- File(FileType type, int readQueueSize, int writeQueueSize) {
- this.type = type;
- readQueue = (readQueueSize > 0 ? new CircularByteBuffer(readQueueSize) : null);
- writeQueue = (writeQueueSize > 0 ? new CircularByteBuffer(writeQueueSize) : null);
- }
-
- void decreaseRefCount() {
- if (openCount <= 0) {
- throw new IllegalStateException();
- }
- openCount--;
- }
-
- void checkNonBlocking(String func) throws IOException {
- if (isBlocking) {
- throw newIOException(func, "File in blocking mode");
- }
- }
- }
-
- static {
- try {
- FD_CONSTRUCTOR = FileDescriptor.class.getDeclaredConstructor(int.class);
- FD_CONSTRUCTOR.setAccessible(true);
-
- Field descriptorIntField;
- try {
- descriptorIntField = FileDescriptor.class.getDeclaredField("descriptor");
- } catch (NoSuchFieldException e) {
- descriptorIntField = FileDescriptor.class.getDeclaredField("fd");
- }
- FD_FIELD_DESCRIPTOR = descriptorIntField;
- FD_FIELD_DESCRIPTOR.setAccessible(true);
-
- PFD_FIELD_DESCRIPTOR = ParcelFileDescriptor.class.getDeclaredField("mFd");
- PFD_FIELD_DESCRIPTOR.setAccessible(true);
-
- PFD_FIELD_GUARD = ParcelFileDescriptor.class.getDeclaredField("mGuard");
- PFD_FIELD_GUARD.setAccessible(true);
-
- CLOSE_GUARD_METHOD_CLOSE = Class.forName("dalvik.system.CloseGuard")
- .getDeclaredMethod("close");
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/async/RateLimiter.java b/common/testutils/devicetests/com/android/testutils/async/RateLimiter.java
deleted file mode 100644
index d5cca0a4..00000000
--- a/common/testutils/devicetests/com/android/testutils/async/RateLimiter.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2023 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.async;
-
-import com.android.net.module.util.async.OsAccess;
-
-import java.util.Arrays;
-
-/**
- * Limits the number of bytes processed to the given maximum of bytes per second.
- *
- * The limiter tracks the total for the past second, along with sums for each 10ms
- * in the past second, allowing the total to be adjusted as the time passes.
- */
-public final class RateLimiter {
- private static final int PERIOD_DURATION_MS = 1000;
- private static final int BUCKET_COUNT = 100;
-
- public static final int BUCKET_DURATION_MS = PERIOD_DURATION_MS / BUCKET_COUNT;
-
- private final OsAccess mOsAccess;
- private final int[] mStatBuckets = new int[BUCKET_COUNT];
- private int mMaxPerPeriodBytes;
- private int mMaxPerBucketBytes;
- private int mRecordedPeriodBytes;
- private long mLastLimitTimestamp;
- private int mLastRequestReduction;
-
- public RateLimiter(OsAccess osAccess, int bytesPerSecond) {
- mOsAccess = osAccess;
- setBytesPerSecond(bytesPerSecond);
- clear();
- }
-
- public int getBytesPerSecond() {
- return mMaxPerPeriodBytes;
- }
-
- public void setBytesPerSecond(int bytesPerSecond) {
- mMaxPerPeriodBytes = bytesPerSecond;
- mMaxPerBucketBytes = Math.max(1, (mMaxPerPeriodBytes / BUCKET_COUNT) * 2);
- }
-
- public void clear() {
- mLastLimitTimestamp = mOsAccess.monotonicTimeMillis();
- mRecordedPeriodBytes = 0;
- Arrays.fill(mStatBuckets, 0);
- }
-
- public static int limit(RateLimiter limiter1, RateLimiter limiter2, int requestedBytes) {
- final long now = limiter1.mOsAccess.monotonicTimeMillis();
- final int allowedCount = Math.min(limiter1.calculateLimit(now, requestedBytes),
- limiter2.calculateLimit(now, requestedBytes));
- limiter1.recordBytes(now, requestedBytes, allowedCount);
- limiter2.recordBytes(now, requestedBytes, allowedCount);
- return allowedCount;
- }
-
- public int limit(int requestedBytes) {
- final long now = mOsAccess.monotonicTimeMillis();
- final int allowedCount = calculateLimit(now, requestedBytes);
- recordBytes(now, requestedBytes, allowedCount);
- return allowedCount;
- }
-
- public int getLastRequestReduction() {
- return mLastRequestReduction;
- }
-
- public boolean acceptAllOrNone(int requestedBytes) {
- final long now = mOsAccess.monotonicTimeMillis();
- final int allowedCount = calculateLimit(now, requestedBytes);
- if (allowedCount < requestedBytes) {
- return false;
- }
- recordBytes(now, requestedBytes, allowedCount);
- return true;
- }
-
- private int calculateLimit(long now, int requestedBytes) {
- // First remove all stale bucket data and adjust the total.
- final long currentBucketAbsIdx = now / BUCKET_DURATION_MS;
- final long staleCutoffIdx = currentBucketAbsIdx - BUCKET_COUNT;
- for (long i = mLastLimitTimestamp / BUCKET_DURATION_MS; i < staleCutoffIdx; i++) {
- final int idx = (int) (i % BUCKET_COUNT);
- mRecordedPeriodBytes -= mStatBuckets[idx];
- mStatBuckets[idx] = 0;
- }
-
- final int bucketIdx = (int) (currentBucketAbsIdx % BUCKET_COUNT);
- final int maxAllowed = Math.min(mMaxPerPeriodBytes - mRecordedPeriodBytes,
- Math.min(mMaxPerBucketBytes - mStatBuckets[bucketIdx], requestedBytes));
- return Math.max(0, maxAllowed);
- }
-
- private void recordBytes(long now, int requestedBytes, int actualBytes) {
- mStatBuckets[(int) ((now / BUCKET_DURATION_MS) % BUCKET_COUNT)] += actualBytes;
- mRecordedPeriodBytes += actualBytes;
- mLastRequestReduction = requestedBytes - actualBytes;
- mLastLimitTimestamp = now;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("{max=");
- sb.append(mMaxPerPeriodBytes);
- sb.append(",max_bucket=");
- sb.append(mMaxPerBucketBytes);
- sb.append(",total=");
- sb.append(mRecordedPeriodBytes);
- sb.append(",last_red=");
- sb.append(mLastRequestReduction);
- sb.append('}');
- return sb.toString();
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/async/ReadableDataAnswer.java b/common/testutils/devicetests/com/android/testutils/async/ReadableDataAnswer.java
deleted file mode 100644
index 4bf55270..00000000
--- a/common/testutils/devicetests/com/android/testutils/async/ReadableDataAnswer.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2023 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.async;
-
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import java.util.ArrayList;
-
-public class ReadableDataAnswer implements Answer {
- private final ArrayList<byte[]> mBuffers = new ArrayList<>();
- private int mBufferPos;
-
- public ReadableDataAnswer(byte[] ... buffers) {
- for (byte[] buffer : buffers) {
- addBuffer(buffer);
- }
- }
-
- public void addBuffer(byte[] buffer) {
- if (buffer.length != 0) {
- mBuffers.add(buffer);
- }
- }
-
- public int getRemainingSize() {
- int totalSize = 0;
- for (byte[] buffer : mBuffers) {
- totalSize += buffer.length;
- }
- return totalSize - mBufferPos;
- }
-
- private void cleanupBuffers() {
- if (!mBuffers.isEmpty() && mBufferPos == mBuffers.get(0).length) {
- mBuffers.remove(0);
- mBufferPos = 0;
- }
- }
-
- @Override
- public Object answer(InvocationOnMock invocation) throws Throwable {
- cleanupBuffers();
-
- if (mBuffers.isEmpty()) {
- return Integer.valueOf(0);
- }
-
- byte[] src = mBuffers.get(0);
-
- byte[] dst = invocation.<byte[]>getArgument(0);
- int dstPos = invocation.<Integer>getArgument(1);
- int dstLen = invocation.<Integer>getArgument(2);
-
- int copyLen = Math.min(dstLen, src.length - mBufferPos);
- System.arraycopy(src, mBufferPos, dst, dstPos, copyLen);
- mBufferPos += copyLen;
-
- cleanupBuffers();
- return Integer.valueOf(copyLen);
- }
-}
diff --git a/common/testutils/devicetests/com/android/testutils/filters/CtsNetTestCasesMaxTargetSdk30.kt b/common/testutils/devicetests/com/android/testutils/filters/CtsNetTestCasesMaxTargetSdk30.kt
deleted file mode 100644
index 843c41e6..00000000
--- a/common/testutils/devicetests/com/android/testutils/filters/CtsNetTestCasesMaxTargetSdk30.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2022 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.filters
-
-/**
- * Only run this test in the CtsNetTestCasesMaxTargetSdk30 suite.
- */
-annotation class CtsNetTestCasesMaxTargetSdk30(val reason: String)
diff --git a/common/testutils/devicetests/com/android/testutils/filters/CtsNetTestCasesMaxTargetSdk31.kt b/common/testutils/devicetests/com/android/testutils/filters/CtsNetTestCasesMaxTargetSdk31.kt
deleted file mode 100644
index be0103d6..00000000
--- a/common/testutils/devicetests/com/android/testutils/filters/CtsNetTestCasesMaxTargetSdk31.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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.filters
-
-/**
- * Only run this test in the CtsNetTestCasesMaxTargetSdk31 suite.
- */
-annotation class CtsNetTestCasesMaxTargetSdk31(val reason: String)
diff --git a/common/testutils/devicetests/com/android/testutils/filters/CtsNetTestCasesMaxTargetSdk33.kt b/common/testutils/devicetests/com/android/testutils/filters/CtsNetTestCasesMaxTargetSdk33.kt
deleted file mode 100644
index 5af890ff..00000000
--- a/common/testutils/devicetests/com/android/testutils/filters/CtsNetTestCasesMaxTargetSdk33.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2023 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.filters
-
-/**
- * Only run this test in the CtsNetTestCasesMaxTargetSdk33 suite.
- */
-annotation class CtsNetTestCasesMaxTargetSdk33(val reason: String)