Merge "Allow collecting diagnostics from shell commands" into main
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index 7c0c223..36c0cf9 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -906,7 +906,12 @@
final InetAddress address, final int protocol, final int remotePort,
final boolean isAllowed) {
throwIfPre25Q2("addLocalNetAccess is not available on pre-B devices");
- final int ifIndex = mDeps.getIfIndex(iface);
+ final int ifIndex;
+ if (iface == null) {
+ ifIndex = 0;
+ } else {
+ ifIndex = mDeps.getIfIndex(iface);
+ }
if (ifIndex == 0) {
Log.e(TAG, "Failed to get if index, skip addLocalNetAccess for " + address
+ "(" + iface + ")");
@@ -935,7 +940,12 @@
public void removeLocalNetAccess(final int lpmBitlen, final String iface,
final InetAddress address, final int protocol, final int remotePort) {
throwIfPre25Q2("removeLocalNetAccess is not available on pre-B devices");
- final int ifIndex = mDeps.getIfIndex(iface);
+ final int ifIndex;
+ if (iface == null) {
+ ifIndex = 0;
+ } else {
+ ifIndex = mDeps.getIfIndex(iface);
+ }
if (ifIndex == 0) {
Log.e(TAG, "Failed to get if index, skip removeLocalNetAccess for " + address
+ "(" + iface + ")");
@@ -966,7 +976,12 @@
public boolean getLocalNetAccess(final int lpmBitlen, final String iface,
final InetAddress address, final int protocol, final int remotePort) {
throwIfPre25Q2("getLocalNetAccess is not available on pre-B devices");
- final int ifIndex = mDeps.getIfIndex(iface);
+ final int ifIndex;
+ if (iface == null) {
+ ifIndex = 0;
+ } else {
+ ifIndex = mDeps.getIfIndex(iface);
+ }
if (ifIndex == 0) {
Log.e(TAG, "Failed to get if index, returning default from getLocalNetAccess for "
+ address + "(" + iface + ")");
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/PollingUtils.kt b/staticlibs/testutils/devicetests/com/android/testutils/PollingUtils.kt
new file mode 100644
index 0000000..0a0290a
--- /dev/null
+++ b/staticlibs/testutils/devicetests/com/android/testutils/PollingUtils.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2025 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
+
+private const val POLLING_INTERVAL_MS: Int = 100
+
+/** Calls condition() until it returns true or timeout occurs. */
+fun pollingCheck(condition: () -> Boolean, timeout_ms: Int): Boolean {
+ var polling_time = 0
+ do {
+ Thread.sleep(POLLING_INTERVAL_MS.toLong())
+ polling_time += POLLING_INTERVAL_MS
+ if (condition()) return true
+ } while (polling_time < timeout_ms)
+ return false
+}
diff --git a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
index c6a1b09..dee5f71 100644
--- a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
+++ b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
@@ -86,6 +86,7 @@
import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
import com.android.testutils.SkipPresubmit
import com.android.testutils.TestableNetworkCallback
+import com.android.testutils.pollingCheck
import com.android.testutils.waitForIdle
import com.google.common.truth.Expect
import com.google.common.truth.Truth.assertThat
@@ -111,7 +112,6 @@
private const val TAG = "ApfIntegrationTest"
private const val TIMEOUT_MS = 2000L
-private const val POLLING_INTERVAL_MS: Int = 100
private const val RCV_BUFFER_SIZE = 1480
private const val PING_HEADER_LENGTH = 8
@@ -129,16 +129,6 @@
private val powerManager = context.getSystemService(PowerManager::class.java)!!
private val wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG)
- fun pollingCheck(condition: () -> Boolean, timeout_ms: Int): Boolean {
- var polling_time = 0
- do {
- Thread.sleep(POLLING_INTERVAL_MS.toLong())
- polling_time += POLLING_INTERVAL_MS
- if (condition()) return true
- } while (polling_time < timeout_ms)
- return false
- }
-
fun turnScreenOff() {
if (!wakeLock.isHeld()) wakeLock.acquire()
runShellCommandOrThrow("input keyevent KEYCODE_SLEEP")
@@ -575,6 +565,13 @@
val program = gen.generate()
assertThat(program.size).isLessThan(counterRegion)
+ val randomProgram = ByteArray(1) { 0 } +
+ ByteArray(counterRegion - 1).also { Random.nextBytes(it) }
+ // There are known firmware bugs where they calculate the number of non-zero bytes within
+ // the program to determine the program length. Modify the test to first install a longer
+ // program before installing a program that do the program length check. This should help us
+ // catch these types of firmware bugs in CTS. (b/395545572)
+ installAndVerifyProgram(randomProgram)
installAndVerifyProgram(program)
// Trigger the program by sending a ping and waiting on the reply.
diff --git a/tests/unit/java/com/android/server/BpfNetMapsTest.java b/tests/unit/java/com/android/server/BpfNetMapsTest.java
index fd92672..caf1765 100644
--- a/tests/unit/java/com/android/server/BpfNetMapsTest.java
+++ b/tests/unit/java/com/android/server/BpfNetMapsTest.java
@@ -266,6 +266,18 @@
@Test
@IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public void testAddLocalNetAccessWithNullInterfaceAfterV() throws Exception {
+ assertTrue(mLocalNetAccessMap.isEmpty());
+
+ mBpfNetMaps.addLocalNetAccess(160, null,
+ Inet4Address.getByName("196.68.0.0"), 0, 0, true);
+
+ // As we tried to add null interface, it would be skipped and map should be empty.
+ assertTrue(mLocalNetAccessMap.isEmpty());
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
public void testAddLocalNetAccessAfterVWithIncorrectInterface() throws Exception {
assertTrue(mLocalNetAccessMap.isEmpty());
@@ -303,6 +315,13 @@
}
@Test
+ @IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public void testGetLocalNetAccessWithNullInterfaceAfterV() throws Exception {
+ assertTrue(mBpfNetMaps.getLocalNetAccess(160, null,
+ Inet4Address.getByName("100.68.0.0"), 0, 0));
+ }
+
+ @Test
@IgnoreAfter(Build.VERSION_CODES.VANILLA_ICE_CREAM)
public void testRemoveLocalNetAccessBeforeV() {
assertThrows(UnsupportedOperationException.class, () ->
@@ -350,6 +369,25 @@
}
@Test
+ @IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public void testRemoveLocalNetAccessAfterVWithNullInterface() throws Exception {
+ assertTrue(mLocalNetAccessMap.isEmpty());
+
+ mBpfNetMaps.addLocalNetAccess(160, TEST_IF_NAME,
+ Inet4Address.getByName("196.68.0.0"), 0, 0, true);
+
+ assertNotNull(mLocalNetAccessMap.getValue(new LocalNetAccessKey(160, TEST_IF_INDEX,
+ Inet4Address.getByName("196.68.0.0"), 0, 0)));
+ assertNull(mLocalNetAccessMap.getValue(new LocalNetAccessKey(160, TEST_IF_INDEX,
+ Inet4Address.getByName("100.68.0.0"), 0, 0)));
+
+ mBpfNetMaps.removeLocalNetAccess(160, null,
+ Inet4Address.getByName("196.68.0.0"), 0, 0);
+ assertNotNull(mLocalNetAccessMap.getValue(new LocalNetAccessKey(160, TEST_IF_INDEX,
+ Inet4Address.getByName("196.68.0.0"), 0, 0)));
+ }
+
+ @Test
@IgnoreAfter(Build.VERSION_CODES.VANILLA_ICE_CREAM)
public void testAddUidToLocalNetBlockMapBeforeV() {
assertThrows(UnsupportedOperationException.class, () ->