Refactor staticlibs am: 9b0df36ea4
Original change: https://android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/3157076
Change-Id: I3a3210caeadee99c4a1c7c4f8fcbc3865b14d283
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 47e5d5e..bcf5e8b 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,7 +1,7 @@
{
"captiveportal-networkstack-resolve-tethering-mainline-presubmit": [
{
- "name": "CtsNetTestCasesLatestSdk",
+ "name": "CtsNetTestCases",
"options": [
{
"exclude-annotation": "com.android.testutils.SkipPresubmit"
@@ -89,7 +89,7 @@
// Test with APK modules only, in cases where APEX is not supported, or the other modules
// were simply not updated
{
- "name": "CtsNetTestCasesLatestSdk",
+ "name": "CtsNetTestCases",
"options": [
{
"exclude-annotation": "com.android.testutils.SkipPresubmit"
@@ -115,7 +115,7 @@
// really exist in the field, but there is no strong guarantee, and it is required by MTS
// testing for module qualification, where modules are tested independently.
{
- "name": "CtsNetTestCasesLatestSdk",
+ "name": "CtsNetTestCases",
"options": [
{
"exclude-annotation": "com.android.testutils.SkipPresubmit"
@@ -158,8 +158,6 @@
},
// Run in addition to mainline-presubmit as mainline-presubmit is not
// supported in every branch.
- // CtsNetTestCasesLatestSdk uses stable API shims, so does not exercise
- // some latest APIs. Run CtsNetTestCases to get coverage of newer APIs.
{
"name": "CtsNetTestCases",
"options": [
@@ -171,18 +169,6 @@
}
]
},
- // Also run CtsNetTestCasesLatestSdk to ensure tests using older shims pass.
- {
- "name": "CtsNetTestCasesLatestSdk",
- "options": [
- {
- "exclude-annotation": "com.android.testutils.SkipPresubmit"
- },
- {
- "exclude-annotation": "androidx.test.filters.RequiresDevice"
- }
- ]
- },
// CTS tests that target older SDKs.
{
"name": "CtsNetTestCasesMaxTargetSdk30",
@@ -275,7 +261,7 @@
],
"mainline-presubmit": [
{
- "name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
+ "name": "CtsNetTestCases[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
"options": [
{
"exclude-annotation": "com.android.testutils.SkipPresubmit"
@@ -333,7 +319,7 @@
// Test with APK modules only, in cases where APEX is not supported, or the other modules
// were simply not updated
{
- "name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk]",
+ "name": "CtsNetTestCases[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk]",
"options": [
{
"exclude-annotation": "com.android.testutils.SkipPresubmit"
@@ -357,7 +343,7 @@
// really exist in the field, but there is no strong guarantee, and it is required by MTS
// testing for module qualification, where modules are tested independently.
{
- "name": "CtsNetTestCasesLatestSdk[com.google.android.tethering.apex]",
+ "name": "CtsNetTestCases[com.google.android.tethering.apex]",
"options": [
{
"exclude-annotation": "com.android.testutils.SkipPresubmit"
@@ -408,7 +394,7 @@
"mainline-postsubmit": [
// Tests on physical devices with SIM cards: postsubmit only for capacity constraints
{
- "name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
+ "name": "CtsNetTestCases[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
"keywords": ["sim"]
},
{
@@ -422,7 +408,7 @@
},
// Postsubmit on virtual devices to monitor flakiness of @SkipMainlinePresubmit methods
{
- "name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
+ "name": "CtsNetTestCases[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
"options": [
{
"exclude-annotation": "androidx.test.filters.RequiresDevice"
diff --git a/Tethering/common/TetheringLib/lint-baseline.xml b/Tethering/common/TetheringLib/lint-baseline.xml
index ed5fbb0..5171efb 100644
--- a/Tethering/common/TetheringLib/lint-baseline.xml
+++ b/Tethering/common/TetheringLib/lint-baseline.xml
@@ -12,4 +12,15 @@
column="50"/>
</issue>
+ <issue
+ id="FlaggedApi"
+ message="Method `TetheringRequest()` is a flagged API and should be inside an `if (Flags.tetheringRequestWithSoftApConfig())` check (or annotate the surrounding method `build` with `@FlaggedApi(Flags.TETHERING_REQUEST_WITH_SOFT_AP_CONFIG) to transfer requirement to caller`)"
+ errorLine1=" return new TetheringRequest(mBuilderParcel);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Connectivity/Tethering/common/TetheringLib/src/android/net/TetheringManager.java"
+ line="814"
+ column="24"/>
+ </issue>
+
</issues>
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 5c258b2..9cdba2f 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -16,6 +16,7 @@
package android.net;
+import static android.Manifest.permission.DUMP;
import static android.net.InetAddresses.parseNumericAddress;
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
import static android.net.TetheringManager.TETHERING_ETHERNET;
@@ -26,36 +27,51 @@
import static android.net.TetheringTester.isExpectedUdpDnsPacket;
import static android.system.OsConstants.ICMP_ECHO;
import static android.system.OsConstants.ICMP_ECHOREPLY;
+import static android.system.OsConstants.IPPROTO_UDP;
import static com.android.net.module.util.ConnectivityUtils.isIPv6ULA;
import static com.android.net.module.util.HexDump.dumpHexString;
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REPLY_TYPE;
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE;
+import static com.android.testutils.DeviceInfoUtils.KVersion;
+import static com.android.testutils.TestPermissionUtil.runAsShell;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
+import android.content.Context;
import android.net.TetheringManager.TetheringRequest;
import android.net.TetheringTester.TetheredDevice;
import android.os.Build;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.VintfRuntimeInfo;
import android.util.Log;
+import android.util.Pair;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.net.module.util.BpfDump;
import com.android.net.module.util.Ipv6Utils;
import com.android.net.module.util.Struct;
+import com.android.net.module.util.bpf.Tether4Key;
+import com.android.net.module.util.bpf.Tether4Value;
+import com.android.net.module.util.bpf.TetherStatsKey;
+import com.android.net.module.util.bpf.TetherStatsValue;
import com.android.net.module.util.structs.Ipv4Header;
import com.android.net.module.util.structs.UdpHeader;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import com.android.testutils.DeviceInfoUtils;
+import com.android.testutils.DumpTestUtils;
import com.android.testutils.NetworkStackModuleTest;
import com.android.testutils.TapPacketReader;
@@ -73,7 +89,9 @@
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeoutException;
@@ -89,6 +107,26 @@
private static final short ICMPECHO_ID = 0x0;
private static final short ICMPECHO_SEQ = 0x0;
+ private static final int DUMP_POLLING_MAX_RETRY = 100;
+ private static final int DUMP_POLLING_INTERVAL_MS = 50;
+ // Kernel treats a confirmed UDP connection which active after two seconds as stream mode.
+ // See upstream commit b7b1d02fc43925a4d569ec221715db2dfa1ce4f5.
+ private static final int UDP_STREAM_TS_MS = 2000;
+ // Give slack time for waiting UDP stream mode because handling conntrack event in user space
+ // may not in precise time. Used to reduce the flaky rate.
+ private static final int UDP_STREAM_SLACK_MS = 500;
+ // Per RX UDP packet size: iphdr (20) + udphdr (8) + payload (2) = 30 bytes.
+ private static final int RX_UDP_PACKET_SIZE = 30;
+ private static final int RX_UDP_PACKET_COUNT = 456;
+ // Per TX UDP packet size: iphdr (20) + udphdr (8) + payload (2) = 30 bytes.
+ private static final int TX_UDP_PACKET_SIZE = 30;
+ private static final int TX_UDP_PACKET_COUNT = 123;
+
+ private static final String DUMPSYS_TETHERING_RAWMAP_ARG = "bpfRawMap";
+ private static final String DUMPSYS_RAWMAP_ARG_STATS = "--stats";
+ private static final String DUMPSYS_RAWMAP_ARG_UPSTREAM4 = "--upstream4";
+ private static final String LINE_DELIMITER = "\\n";
+
// TODO: use class DnsPacket to build DNS query and reply message once DnsPacket supports
// building packet for given arguments.
private static final ByteBuffer DNS_QUERY = ByteBuffer.wrap(new byte[] {
@@ -802,4 +840,217 @@
final MacAddress macAddress = MacAddress.fromString("11:22:33:44:55:66");
assertTrue(tester.testDhcpServerAlive(macAddress));
}
+
+ private static boolean isUdpOffloadSupportedByKernel(final String kernelVersion) {
+ final KVersion current = DeviceInfoUtils.getMajorMinorSubminorVersion(kernelVersion);
+ return current.isInRange(new KVersion(4, 14, 222), new KVersion(4, 19, 0))
+ || current.isInRange(new KVersion(4, 19, 176), new KVersion(5, 4, 0))
+ || current.isAtLeast(new KVersion(5, 4, 98));
+ }
+
+ @Test
+ public void testIsUdpOffloadSupportedByKernel() throws Exception {
+ assertFalse(isUdpOffloadSupportedByKernel("4.14.221"));
+ assertTrue(isUdpOffloadSupportedByKernel("4.14.222"));
+ assertTrue(isUdpOffloadSupportedByKernel("4.16.0"));
+ assertTrue(isUdpOffloadSupportedByKernel("4.18.0"));
+ assertFalse(isUdpOffloadSupportedByKernel("4.19.0"));
+
+ assertFalse(isUdpOffloadSupportedByKernel("4.19.175"));
+ assertTrue(isUdpOffloadSupportedByKernel("4.19.176"));
+ assertTrue(isUdpOffloadSupportedByKernel("5.2.0"));
+ assertTrue(isUdpOffloadSupportedByKernel("5.3.0"));
+ assertFalse(isUdpOffloadSupportedByKernel("5.4.0"));
+
+ assertFalse(isUdpOffloadSupportedByKernel("5.4.97"));
+ assertTrue(isUdpOffloadSupportedByKernel("5.4.98"));
+ assertTrue(isUdpOffloadSupportedByKernel("5.10.0"));
+ }
+
+ private static void assumeKernelSupportBpfOffloadUdpV4() {
+ final String kernelVersion = VintfRuntimeInfo.getKernelVersion();
+ assumeTrue("Kernel version " + kernelVersion + " doesn't support IPv4 UDP BPF offload",
+ isUdpOffloadSupportedByKernel(kernelVersion));
+ }
+
+ @Test
+ public void testKernelSupportBpfOffloadUdpV4() throws Exception {
+ assumeKernelSupportBpfOffloadUdpV4();
+ }
+
+ private boolean isTetherConfigBpfOffloadEnabled() throws Exception {
+ final String dumpStr = runAsShell(DUMP, () ->
+ DumpTestUtils.dumpService(Context.TETHERING_SERVICE, "--short"));
+
+ // BPF offload tether config can be overridden by "config_tether_enable_bpf_offload" in
+ // packages/modules/Connectivity/Tethering/res/values/config.xml. OEM may disable config by
+ // RRO to override the enabled default value. Get the tethering config via dumpsys.
+ // $ dumpsys tethering
+ // mIsBpfEnabled: true
+ boolean enabled = dumpStr.contains("mIsBpfEnabled: true");
+ if (!enabled) {
+ Log.d(TAG, "BPF offload tether config not enabled: " + dumpStr);
+ }
+ return enabled;
+ }
+
+ @Test
+ public void testTetherConfigBpfOffloadEnabled() throws Exception {
+ assumeTrue(isTetherConfigBpfOffloadEnabled());
+ }
+
+ @NonNull
+ private <K extends Struct, V extends Struct> HashMap<K, V> dumpAndParseRawMap(
+ Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg)
+ throws Exception {
+ final String[] args = new String[] {DUMPSYS_TETHERING_RAWMAP_ARG, mapArg};
+ final String rawMapStr = runAsShell(DUMP, () ->
+ DumpTestUtils.dumpService(Context.TETHERING_SERVICE, args));
+ final HashMap<K, V> map = new HashMap<>();
+
+ for (final String line : rawMapStr.split(LINE_DELIMITER)) {
+ final Pair<K, V> rule =
+ BpfDump.fromBase64EncodedString(keyClass, valueClass, line.trim());
+ map.put(rule.first, rule.second);
+ }
+ return map;
+ }
+
+ @Nullable
+ private <K extends Struct, V extends Struct> HashMap<K, V> pollRawMapFromDump(
+ Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg)
+ throws Exception {
+ for (int retryCount = 0; retryCount < DUMP_POLLING_MAX_RETRY; retryCount++) {
+ final HashMap<K, V> map = dumpAndParseRawMap(keyClass, valueClass, mapArg);
+ if (!map.isEmpty()) return map;
+
+ Thread.sleep(DUMP_POLLING_INTERVAL_MS);
+ }
+
+ fail("Cannot get rules after " + DUMP_POLLING_MAX_RETRY * DUMP_POLLING_INTERVAL_MS + "ms");
+ return null;
+ }
+
+ // Test network topology:
+ //
+ // public network (rawip) private network
+ // | UE |
+ // +------------+ V +------------+------------+ V +------------+
+ // | Sever +---------+ Upstream | Downstream +---------+ Client |
+ // +------------+ +------------+------------+ +------------+
+ // remote ip public ip private ip
+ // 8.8.8.8:443 <Upstream ip>:9876 <TetheredDevice ip>:9876
+ //
+ private void runUdp4Test() throws Exception {
+ final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR),
+ toList(TEST_IP4_DNS));
+ final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */);
+
+ // TODO: remove the connectivity verification for upstream connected notification race.
+ // Because async upstream connected notification can't guarantee the tethering routing is
+ // ready to use. Need to test tethering connectivity before testing.
+ // For short term plan, consider using IPv6 RA to get MAC address because the prefix comes
+ // from upstream. That can guarantee that the routing is ready. Long term plan is that
+ // refactors upstream connected notification from async to sync.
+ probeV4TetheringConnectivity(tester, tethered, false /* is4To6 */);
+
+ final MacAddress srcMac = tethered.macAddr;
+ final MacAddress dstMac = tethered.routerMacAddr;
+ final InetAddress remoteIp = REMOTE_IP4_ADDR;
+ final InetAddress tetheringUpstreamIp = TEST_IP4_ADDR.getAddress();
+ final InetAddress clientIp = tethered.ipv4Addr;
+ sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester, false /* is4To6 */);
+ sendDownloadPacketUdp(remoteIp, tetheringUpstreamIp, tester, false /* is6To4 */);
+
+ // Send second UDP packet in original direction.
+ // The BPF coordinator only offloads the ASSURED conntrack entry. The "request + reply"
+ // packets can make status IPS_SEEN_REPLY to be set. Need one more packet to make
+ // conntrack status IPS_ASSURED_BIT to be set. Note the third packet needs to delay
+ // 2 seconds because kernel monitors a UDP connection which still alive after 2 seconds
+ // and apply ASSURED flag.
+ // See kernel upstream commit b7b1d02fc43925a4d569ec221715db2dfa1ce4f5 and
+ // nf_conntrack_udp_packet in net/netfilter/nf_conntrack_proto_udp.c
+ Thread.sleep(UDP_STREAM_TS_MS);
+ sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester, false /* is4To6 */);
+
+ // Give a slack time for handling conntrack event in user space.
+ Thread.sleep(UDP_STREAM_SLACK_MS);
+
+ // [1] Verify IPv4 upstream rule map.
+ final HashMap<Tether4Key, Tether4Value> upstreamMap = pollRawMapFromDump(
+ Tether4Key.class, Tether4Value.class, DUMPSYS_RAWMAP_ARG_UPSTREAM4);
+ assertNotNull(upstreamMap);
+ assertEquals(1, upstreamMap.size());
+
+ final Map.Entry<Tether4Key, Tether4Value> rule =
+ upstreamMap.entrySet().iterator().next();
+
+ final Tether4Key upstream4Key = rule.getKey();
+ assertEquals(IPPROTO_UDP, upstream4Key.l4proto);
+ assertTrue(Arrays.equals(tethered.ipv4Addr.getAddress(), upstream4Key.src4));
+ assertEquals(LOCAL_PORT, upstream4Key.srcPort);
+ assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(), upstream4Key.dst4));
+ assertEquals(REMOTE_PORT, upstream4Key.dstPort);
+
+ final Tether4Value upstream4Value = rule.getValue();
+ assertTrue(Arrays.equals(tetheringUpstreamIp.getAddress(),
+ InetAddress.getByAddress(upstream4Value.src46).getAddress()));
+ assertEquals(LOCAL_PORT, upstream4Value.srcPort);
+ assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(),
+ InetAddress.getByAddress(upstream4Value.dst46).getAddress()));
+ assertEquals(REMOTE_PORT, upstream4Value.dstPort);
+
+ // [2] Verify stats map.
+ // Transmit packets on both direction for verifying stats. Because we only care the
+ // packet count in stats test, we just reuse the existing packets to increaes
+ // the packet count on both direction.
+
+ // Send packets on original direction.
+ for (int i = 0; i < TX_UDP_PACKET_COUNT; i++) {
+ sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester,
+ false /* is4To6 */);
+ }
+
+ // Send packets on reply direction.
+ for (int i = 0; i < RX_UDP_PACKET_COUNT; i++) {
+ sendDownloadPacketUdp(remoteIp, tetheringUpstreamIp, tester, false /* is6To4 */);
+ }
+
+ // Dump stats map to verify.
+ final HashMap<TetherStatsKey, TetherStatsValue> statsMap = pollRawMapFromDump(
+ TetherStatsKey.class, TetherStatsValue.class, DUMPSYS_RAWMAP_ARG_STATS);
+ assertNotNull(statsMap);
+ assertEquals(1, statsMap.size());
+
+ final Map.Entry<TetherStatsKey, TetherStatsValue> stats =
+ statsMap.entrySet().iterator().next();
+
+ // TODO: verify the upstream index in TetherStatsKey.
+
+ final TetherStatsValue statsValue = stats.getValue();
+ assertEquals(RX_UDP_PACKET_COUNT, statsValue.rxPackets);
+ assertEquals(RX_UDP_PACKET_COUNT * RX_UDP_PACKET_SIZE, statsValue.rxBytes);
+ assertEquals(0, statsValue.rxErrors);
+ assertEquals(TX_UDP_PACKET_COUNT, statsValue.txPackets);
+ assertEquals(TX_UDP_PACKET_COUNT * TX_UDP_PACKET_SIZE, statsValue.txBytes);
+ assertEquals(0, statsValue.txErrors);
+ }
+
+ /**
+ * BPF offload IPv4 UDP tethering test. Verify that UDP tethered packets are offloaded by BPF.
+ * Minimum test requirement:
+ * 1. S+ device.
+ * 2. Tethering config enables tethering BPF offload.
+ * 3. Kernel supports IPv4 UDP BPF offload. See #isUdpOffloadSupportedByKernel.
+ *
+ * TODO: consider enabling the test even tethering config disables BPF offload. See b/238288883
+ */
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testTetherBpfOffloadUdpV4() throws Exception {
+ assumeTrue("Tethering config disabled BPF offload", isTetherConfigBpfOffloadEnabled());
+ assumeKernelSupportBpfOffloadUdpV4();
+
+ runUdp4Test();
+ }
}
diff --git a/Tethering/tests/mts/src/android/tethering/mts/MtsEthernetTetheringTest.java b/Tethering/tests/mts/src/android/tethering/mts/MtsEthernetTetheringTest.java
deleted file mode 100644
index c2bc812..0000000
--- a/Tethering/tests/mts/src/android/tethering/mts/MtsEthernetTetheringTest.java
+++ /dev/null
@@ -1,304 +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 android.net;
-
-import static android.Manifest.permission.DUMP;
-import static android.system.OsConstants.IPPROTO_UDP;
-
-import static com.android.testutils.DeviceInfoUtils.KVersion;
-import static com.android.testutils.TestPermissionUtil.runAsShell;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
-
-import android.content.Context;
-import android.net.TetheringTester.TetheredDevice;
-import android.os.Build;
-import android.os.VintfRuntimeInfo;
-import android.util.Log;
-import android.util.Pair;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.net.module.util.BpfDump;
-import com.android.net.module.util.Struct;
-import com.android.net.module.util.bpf.Tether4Key;
-import com.android.net.module.util.bpf.Tether4Value;
-import com.android.net.module.util.bpf.TetherStatsKey;
-import com.android.net.module.util.bpf.TetherStatsValue;
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
-import com.android.testutils.DeviceInfoUtils;
-import com.android.testutils.DumpTestUtils;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.net.InetAddress;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-
-@RunWith(AndroidJUnit4.class)
-@MediumTest
-public class MtsEthernetTetheringTest extends EthernetTetheringTestBase {
- @Rule
- public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
-
- private static final String TAG = MtsEthernetTetheringTest.class.getSimpleName();
-
- private static final int DUMP_POLLING_MAX_RETRY = 100;
- private static final int DUMP_POLLING_INTERVAL_MS = 50;
- // Kernel treats a confirmed UDP connection which active after two seconds as stream mode.
- // See upstream commit b7b1d02fc43925a4d569ec221715db2dfa1ce4f5.
- private static final int UDP_STREAM_TS_MS = 2000;
- // Give slack time for waiting UDP stream mode because handling conntrack event in user space
- // may not in precise time. Used to reduce the flaky rate.
- private static final int UDP_STREAM_SLACK_MS = 500;
- // Per RX UDP packet size: iphdr (20) + udphdr (8) + payload (2) = 30 bytes.
- private static final int RX_UDP_PACKET_SIZE = 30;
- private static final int RX_UDP_PACKET_COUNT = 456;
- // Per TX UDP packet size: iphdr (20) + udphdr (8) + payload (2) = 30 bytes.
- private static final int TX_UDP_PACKET_SIZE = 30;
- private static final int TX_UDP_PACKET_COUNT = 123;
-
- private static final String DUMPSYS_TETHERING_RAWMAP_ARG = "bpfRawMap";
- private static final String DUMPSYS_RAWMAP_ARG_STATS = "--stats";
- private static final String DUMPSYS_RAWMAP_ARG_UPSTREAM4 = "--upstream4";
- private static final String LINE_DELIMITER = "\\n";
-
- private static boolean isUdpOffloadSupportedByKernel(final String kernelVersion) {
- final KVersion current = DeviceInfoUtils.getMajorMinorSubminorVersion(kernelVersion);
- return current.isInRange(new KVersion(4, 14, 222), new KVersion(4, 19, 0))
- || current.isInRange(new KVersion(4, 19, 176), new KVersion(5, 4, 0))
- || current.isAtLeast(new KVersion(5, 4, 98));
- }
-
- @Test
- public void testIsUdpOffloadSupportedByKernel() throws Exception {
- assertFalse(isUdpOffloadSupportedByKernel("4.14.221"));
- assertTrue(isUdpOffloadSupportedByKernel("4.14.222"));
- assertTrue(isUdpOffloadSupportedByKernel("4.16.0"));
- assertTrue(isUdpOffloadSupportedByKernel("4.18.0"));
- assertFalse(isUdpOffloadSupportedByKernel("4.19.0"));
-
- assertFalse(isUdpOffloadSupportedByKernel("4.19.175"));
- assertTrue(isUdpOffloadSupportedByKernel("4.19.176"));
- assertTrue(isUdpOffloadSupportedByKernel("5.2.0"));
- assertTrue(isUdpOffloadSupportedByKernel("5.3.0"));
- assertFalse(isUdpOffloadSupportedByKernel("5.4.0"));
-
- assertFalse(isUdpOffloadSupportedByKernel("5.4.97"));
- assertTrue(isUdpOffloadSupportedByKernel("5.4.98"));
- assertTrue(isUdpOffloadSupportedByKernel("5.10.0"));
- }
-
- private static void assumeKernelSupportBpfOffloadUdpV4() {
- final String kernelVersion = VintfRuntimeInfo.getKernelVersion();
- assumeTrue("Kernel version " + kernelVersion + " doesn't support IPv4 UDP BPF offload",
- isUdpOffloadSupportedByKernel(kernelVersion));
- }
-
- @Test
- public void testKernelSupportBpfOffloadUdpV4() throws Exception {
- assumeKernelSupportBpfOffloadUdpV4();
- }
-
- private boolean isTetherConfigBpfOffloadEnabled() throws Exception {
- final String dumpStr = runAsShell(DUMP, () ->
- DumpTestUtils.dumpService(Context.TETHERING_SERVICE, "--short"));
-
- // BPF offload tether config can be overridden by "config_tether_enable_bpf_offload" in
- // packages/modules/Connectivity/Tethering/res/values/config.xml. OEM may disable config by
- // RRO to override the enabled default value. Get the tethering config via dumpsys.
- // $ dumpsys tethering
- // mIsBpfEnabled: true
- boolean enabled = dumpStr.contains("mIsBpfEnabled: true");
- if (!enabled) {
- Log.d(TAG, "BPF offload tether config not enabled: " + dumpStr);
- }
- return enabled;
- }
-
- @Test
- public void testTetherConfigBpfOffloadEnabled() throws Exception {
- assumeTrue(isTetherConfigBpfOffloadEnabled());
- }
-
- @NonNull
- private <K extends Struct, V extends Struct> HashMap<K, V> dumpAndParseRawMap(
- Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg)
- throws Exception {
- final String[] args = new String[] {DUMPSYS_TETHERING_RAWMAP_ARG, mapArg};
- final String rawMapStr = runAsShell(DUMP, () ->
- DumpTestUtils.dumpService(Context.TETHERING_SERVICE, args));
- final HashMap<K, V> map = new HashMap<>();
-
- for (final String line : rawMapStr.split(LINE_DELIMITER)) {
- final Pair<K, V> rule =
- BpfDump.fromBase64EncodedString(keyClass, valueClass, line.trim());
- map.put(rule.first, rule.second);
- }
- return map;
- }
-
- @Nullable
- private <K extends Struct, V extends Struct> HashMap<K, V> pollRawMapFromDump(
- Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg)
- throws Exception {
- for (int retryCount = 0; retryCount < DUMP_POLLING_MAX_RETRY; retryCount++) {
- final HashMap<K, V> map = dumpAndParseRawMap(keyClass, valueClass, mapArg);
- if (!map.isEmpty()) return map;
-
- Thread.sleep(DUMP_POLLING_INTERVAL_MS);
- }
-
- fail("Cannot get rules after " + DUMP_POLLING_MAX_RETRY * DUMP_POLLING_INTERVAL_MS + "ms");
- return null;
- }
-
- // Test network topology:
- //
- // public network (rawip) private network
- // | UE |
- // +------------+ V +------------+------------+ V +------------+
- // | Sever +---------+ Upstream | Downstream +---------+ Client |
- // +------------+ +------------+------------+ +------------+
- // remote ip public ip private ip
- // 8.8.8.8:443 <Upstream ip>:9876 <TetheredDevice ip>:9876
- //
- private void runUdp4Test() throws Exception {
- final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR),
- toList(TEST_IP4_DNS));
- final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */);
-
- // TODO: remove the connectivity verification for upstream connected notification race.
- // Because async upstream connected notification can't guarantee the tethering routing is
- // ready to use. Need to test tethering connectivity before testing.
- // For short term plan, consider using IPv6 RA to get MAC address because the prefix comes
- // from upstream. That can guarantee that the routing is ready. Long term plan is that
- // refactors upstream connected notification from async to sync.
- probeV4TetheringConnectivity(tester, tethered, false /* is4To6 */);
-
- final MacAddress srcMac = tethered.macAddr;
- final MacAddress dstMac = tethered.routerMacAddr;
- final InetAddress remoteIp = REMOTE_IP4_ADDR;
- final InetAddress tetheringUpstreamIp = TEST_IP4_ADDR.getAddress();
- final InetAddress clientIp = tethered.ipv4Addr;
- sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester, false /* is4To6 */);
- sendDownloadPacketUdp(remoteIp, tetheringUpstreamIp, tester, false /* is6To4 */);
-
- // Send second UDP packet in original direction.
- // The BPF coordinator only offloads the ASSURED conntrack entry. The "request + reply"
- // packets can make status IPS_SEEN_REPLY to be set. Need one more packet to make
- // conntrack status IPS_ASSURED_BIT to be set. Note the third packet needs to delay
- // 2 seconds because kernel monitors a UDP connection which still alive after 2 seconds
- // and apply ASSURED flag.
- // See kernel upstream commit b7b1d02fc43925a4d569ec221715db2dfa1ce4f5 and
- // nf_conntrack_udp_packet in net/netfilter/nf_conntrack_proto_udp.c
- Thread.sleep(UDP_STREAM_TS_MS);
- sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester, false /* is4To6 */);
-
- // Give a slack time for handling conntrack event in user space.
- Thread.sleep(UDP_STREAM_SLACK_MS);
-
- // [1] Verify IPv4 upstream rule map.
- final HashMap<Tether4Key, Tether4Value> upstreamMap = pollRawMapFromDump(
- Tether4Key.class, Tether4Value.class, DUMPSYS_RAWMAP_ARG_UPSTREAM4);
- assertNotNull(upstreamMap);
- assertEquals(1, upstreamMap.size());
-
- final Map.Entry<Tether4Key, Tether4Value> rule =
- upstreamMap.entrySet().iterator().next();
-
- final Tether4Key upstream4Key = rule.getKey();
- assertEquals(IPPROTO_UDP, upstream4Key.l4proto);
- assertTrue(Arrays.equals(tethered.ipv4Addr.getAddress(), upstream4Key.src4));
- assertEquals(LOCAL_PORT, upstream4Key.srcPort);
- assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(), upstream4Key.dst4));
- assertEquals(REMOTE_PORT, upstream4Key.dstPort);
-
- final Tether4Value upstream4Value = rule.getValue();
- assertTrue(Arrays.equals(tetheringUpstreamIp.getAddress(),
- InetAddress.getByAddress(upstream4Value.src46).getAddress()));
- assertEquals(LOCAL_PORT, upstream4Value.srcPort);
- assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(),
- InetAddress.getByAddress(upstream4Value.dst46).getAddress()));
- assertEquals(REMOTE_PORT, upstream4Value.dstPort);
-
- // [2] Verify stats map.
- // Transmit packets on both direction for verifying stats. Because we only care the
- // packet count in stats test, we just reuse the existing packets to increaes
- // the packet count on both direction.
-
- // Send packets on original direction.
- for (int i = 0; i < TX_UDP_PACKET_COUNT; i++) {
- sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester,
- false /* is4To6 */);
- }
-
- // Send packets on reply direction.
- for (int i = 0; i < RX_UDP_PACKET_COUNT; i++) {
- sendDownloadPacketUdp(remoteIp, tetheringUpstreamIp, tester, false /* is6To4 */);
- }
-
- // Dump stats map to verify.
- final HashMap<TetherStatsKey, TetherStatsValue> statsMap = pollRawMapFromDump(
- TetherStatsKey.class, TetherStatsValue.class, DUMPSYS_RAWMAP_ARG_STATS);
- assertNotNull(statsMap);
- assertEquals(1, statsMap.size());
-
- final Map.Entry<TetherStatsKey, TetherStatsValue> stats =
- statsMap.entrySet().iterator().next();
-
- // TODO: verify the upstream index in TetherStatsKey.
-
- final TetherStatsValue statsValue = stats.getValue();
- assertEquals(RX_UDP_PACKET_COUNT, statsValue.rxPackets);
- assertEquals(RX_UDP_PACKET_COUNT * RX_UDP_PACKET_SIZE, statsValue.rxBytes);
- assertEquals(0, statsValue.rxErrors);
- assertEquals(TX_UDP_PACKET_COUNT, statsValue.txPackets);
- assertEquals(TX_UDP_PACKET_COUNT * TX_UDP_PACKET_SIZE, statsValue.txBytes);
- assertEquals(0, statsValue.txErrors);
- }
-
- /**
- * BPF offload IPv4 UDP tethering test. Verify that UDP tethered packets are offloaded by BPF.
- * Minimum test requirement:
- * 1. S+ device.
- * 2. Tethering config enables tethering BPF offload.
- * 3. Kernel supports IPv4 UDP BPF offload. See #isUdpOffloadSupportedByKernel.
- *
- * TODO: consider enabling the test even tethering config disables BPF offload. See b/238288883
- */
- @Test
- @IgnoreUpTo(Build.VERSION_CODES.R)
- public void testTetherBpfOffloadUdpV4() throws Exception {
- assumeTrue("Tethering config disabled BPF offload", isTetherConfigBpfOffloadEnabled());
- assumeKernelSupportBpfOffloadUdpV4();
-
- runUdp4Test();
- }
-}
diff --git a/service/ServiceConnectivityResources/res/values-ky/strings.xml b/service/ServiceConnectivityResources/res/values-ky/strings.xml
index 08ffd2a..398531a 100644
--- a/service/ServiceConnectivityResources/res/values-ky/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ky/strings.xml
@@ -26,7 +26,7 @@
<string name="mobile_network_available_no_internet_detailed" msgid="5438738723127062816">"<xliff:g id="NETWORK_CARRIER">%1$s</xliff:g> трафиги түгөнгөн окшойт. Параметрлерди ачуу үчүн таптаңыз."</string>
<string name="mobile_network_available_no_internet_detailed_unknown_carrier" msgid="5375681117265354337">"Трафик түгөнгөн окшойт. Параметрлерди ачуу үчүн таптаңыз."</string>
<string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> Интернетке туташуусу жок"</string>
- <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Параметрлерди ачуу үчүн таптап коюңуз"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Параметрлерди ачуу үчүн тийип коюңуз"</string>
<string name="mobile_no_internet" msgid="4087718456753201450">"Мобилдик Интернет жок"</string>
<string name="other_networks_no_internet" msgid="5693932964749676542">"Тармактын Интернет жок"</string>
<string name="private_dns_broken_detailed" msgid="2677123850463207823">"Жеке DNS сервери жеткиликсиз"</string>
diff --git a/service/ServiceConnectivityResources/res/values-mn/strings.xml b/service/ServiceConnectivityResources/res/values-mn/strings.xml
index 2f13ef4..9af035b 100644
--- a/service/ServiceConnectivityResources/res/values-mn/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-mn/strings.xml
@@ -27,7 +27,7 @@
<string name="mobile_network_available_no_internet_detailed_unknown_carrier" msgid="5375681117265354337">"Таны дата дууссан байж магадгүй. Сонголтыг харахын тулд товшино уу."</string>
<string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-д интернэтийн хандалт алга"</string>
<string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Сонголт хийхийн тулд товшино уу"</string>
- <string name="mobile_no_internet" msgid="4087718456753201450">"Мобайл сүлжээнд интернэт хандалт байхгүй байна"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Хөдөлгөөнт холбооны сүлжээнд интернэт хандалт байхгүй байна"</string>
<string name="other_networks_no_internet" msgid="5693932964749676542">"Сүлжээнд интернэт хандалт байхгүй байна"</string>
<string name="private_dns_broken_detailed" msgid="2677123850463207823">"Хувийн DNS серверт хандах боломжгүй байна"</string>
<string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> зарим үйлчилгээнд хандах боломжгүй байна"</string>
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt b/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt
index 66362d4..ae43c15 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt
@@ -44,13 +44,18 @@
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 DEFAULT_TAG = RecorderCallback::class.simpleName
+ ?: fail("Could not determine class name")
- private val TAG = this::class.simpleName
+open class RecorderCallback private constructor(
+ private val backingRecord: ArrayTrackRecord<CallbackEntry>,
+ val logTag: String
+) : NetworkCallback() {
+ public constructor(logTag: String = DEFAULT_TAG) : this(ArrayTrackRecord(), logTag)
+ protected constructor(src: RecorderCallback?, logTag: String) : this(
+ src?.backingRecord ?: ArrayTrackRecord(),
+ logTag
+ )
sealed class CallbackEntry {
// To get equals(), hashcode(), componentN() etc for free, the child classes of
@@ -123,7 +128,7 @@
val mark get() = history.mark
override fun onAvailable(network: Network) {
- Log.d(TAG, "onAvailable $network")
+ Log.d(logTag, "onAvailable $network")
history.add(Available(network))
}
@@ -131,22 +136,22 @@
// 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")
+ Log.d(logTag, "onCapabilitiesChanged $network $caps")
history.add(CapabilitiesChanged(network, caps))
}
override fun onLinkPropertiesChanged(network: Network, lp: LinkProperties) {
- Log.d(TAG, "onLinkPropertiesChanged $network $lp")
+ Log.d(logTag, "onLinkPropertiesChanged $network $lp")
history.add(LinkPropertiesChanged(network, lp))
}
override fun onLocalNetworkInfoChanged(network: Network, info: LocalNetworkInfo) {
- Log.d(TAG, "onLocalNetworkInfoChanged $network $info")
+ Log.d(logTag, "onLocalNetworkInfoChanged $network $info")
history.add(LocalInfoChanged(network, info))
}
override fun onBlockedStatusChanged(network: Network, blocked: Boolean) {
- Log.d(TAG, "onBlockedStatusChanged $network $blocked")
+ Log.d(logTag, "onBlockedStatusChanged $network $blocked")
history.add(BlockedStatus(network, blocked))
}
@@ -154,27 +159,27 @@
// 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")
+ Log.d(logTag, "onNetworkSuspended $network $network")
history.add(Suspended(network))
}
override fun onNetworkResumed(network: Network) {
- Log.d(TAG, "$network onNetworkResumed $network")
+ Log.d(logTag, "$network onNetworkResumed $network")
history.add(Resumed(network))
}
override fun onLosing(network: Network, maxMsToLive: Int) {
- Log.d(TAG, "onLosing $network $maxMsToLive")
+ Log.d(logTag, "onLosing $network $maxMsToLive")
history.add(Losing(network, maxMsToLive))
}
override fun onLost(network: Network) {
- Log.d(TAG, "onLost $network")
+ Log.d(logTag, "onLost $network")
history.add(Lost(network))
}
override fun onUnavailable() {
- Log.d(TAG, "onUnavailable")
+ Log.d(logTag, "onUnavailable")
history.add(Unavailable())
}
}
@@ -188,10 +193,11 @@
*/
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) {
+ val defaultTimeoutMs: Long,
+ val defaultNoCallbackTimeoutMs: Long,
+ val waiterFunc: Runnable,
+ logTag: String
+) : RecorderCallback(src, logTag) {
/**
* Construct a testable network callback.
* @param timeoutMs the default timeout for expecting a callback. Default 30 seconds. This
@@ -213,14 +219,16 @@
constructor(
timeoutMs: Long = DEFAULT_TIMEOUT,
noCallbackTimeoutMs: Long = DEFAULT_NO_CALLBACK_TIMEOUT,
- waiterFunc: Runnable = NOOP
- ) : this(null, timeoutMs, noCallbackTimeoutMs, waiterFunc)
+ waiterFunc: Runnable = NOOP, // "() -> Unit" would forbid calling with a void func from Java
+ logTag: String = DEFAULT_TAG
+ ) : this(null, timeoutMs, noCallbackTimeoutMs, waiterFunc, logTag)
fun createLinkedCopy() = TestableNetworkCallback(
this,
defaultTimeoutMs,
defaultNoCallbackTimeoutMs,
- waiterFunc
+ waiterFunc,
+ logTag
)
// The last available network, or null if any network was lost since the last call to
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index ae85701..1cd8327 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -111,25 +111,6 @@
min_sdk_version: "30",
}
-// Networking CTS tests that target the latest released SDK. These tests can be installed on release
-// devices at any point in the Android release cycle and are useful for qualifying mainline modules
-// on release devices.
-android_test {
- name: "CtsNetTestCasesLatestSdk",
- defaults: [
- "ConnectivityTestsLatestSdkDefaults",
- "CtsNetTestCasesDefaults",
- "CtsNetTestCasesApiStableDefaults",
- ],
- test_suites: [
- "general-tests",
- "mts-dnsresolver",
- "mts-networking",
- "mts-tethering",
- "mts-wifi",
- ],
-}
-
java_defaults {
name: "CtsNetTestCasesMaxTargetSdkDefaults",
defaults: [
diff --git a/tests/cts/net/AndroidTestTemplate.xml b/tests/cts/net/AndroidTestTemplate.xml
index 077c3ef..024d3bf 100644
--- a/tests/cts/net/AndroidTestTemplate.xml
+++ b/tests/cts/net/AndroidTestTemplate.xml
@@ -48,8 +48,8 @@
those tests with an annotation matching the name of the APK.
This allows us to maintain one AndroidTestTemplate.xml for all CtsNetTestCases*.apk,
- and have CtsNetTestCases and CtsNetTestCasesLatestSdk run all tests, but have
- CtsNetTestCasesMaxTargetSdk31 run only tests that require target SDK 31.
+ and have CtsNetTestCases run all tests, but have CtsNetTestCasesMaxTargetSdk31 run only
+ tests that require target SDK 31.
This relies on the fact that if the class specified in include-annotation exists, then
the runner will only run the tests annotated with that annotation, but if it does not,