Merge "Remove NetHttpTests" into main
diff --git a/DnsResolver/DnsBpfHelper.cpp b/DnsResolver/DnsBpfHelper.cpp
index de8bef5..0719ade 100644
--- a/DnsResolver/DnsBpfHelper.cpp
+++ b/DnsResolver/DnsBpfHelper.cpp
@@ -69,9 +69,10 @@
// state, making it a trustworthy source. Since this library primarily serves DNS resolvers,
// relying solely on V+ data prevents erroneous blocking of DNS queries.
if (android::modules::sdklevel::IsAtLeastV() && metered) {
- // The background data setting (PENALTY_BOX_MATCH) and unrestricted data usage setting
- // (HAPPY_BOX_MATCH) for individual apps override the system wide Data Saver setting.
- if (uidRules & PENALTY_BOX_MATCH) return true;
+ // The background data setting (PENALTY_BOX_USER_MATCH, PENALTY_BOX_ADMIN_MATCH) and
+ // unrestricted data usage setting (HAPPY_BOX_MATCH) for individual apps override the system
+ // wide Data Saver setting.
+ if (uidRules & (PENALTY_BOX_USER_MATCH | PENALTY_BOX_ADMIN_MATCH)) return true;
if (uidRules & HAPPY_BOX_MATCH) return false;
auto dataSaverSetting = mDataSaverEnabledMap.readValue(DATA_SAVER_ENABLED_KEY);
diff --git a/DnsResolver/DnsBpfHelperTest.cpp b/DnsResolver/DnsBpfHelperTest.cpp
index 67b5b95..18a5df4 100644
--- a/DnsResolver/DnsBpfHelperTest.cpp
+++ b/DnsResolver/DnsBpfHelperTest.cpp
@@ -158,23 +158,33 @@
}
} testConfigs[]{
// clang-format off
- // enabledRules, dataSaverEnabled, uidRules, blocked
- {NO_MATCH, false, NO_MATCH, false},
- {NO_MATCH, false, PENALTY_BOX_MATCH, true},
- {NO_MATCH, false, HAPPY_BOX_MATCH, false},
- {NO_MATCH, false, PENALTY_BOX_MATCH|HAPPY_BOX_MATCH, true},
- {NO_MATCH, true, NO_MATCH, true},
- {NO_MATCH, true, PENALTY_BOX_MATCH, true},
- {NO_MATCH, true, HAPPY_BOX_MATCH, false},
- {NO_MATCH, true, PENALTY_BOX_MATCH|HAPPY_BOX_MATCH, true},
- {STANDBY_MATCH, false, STANDBY_MATCH, true},
- {STANDBY_MATCH, false, STANDBY_MATCH|PENALTY_BOX_MATCH, true},
- {STANDBY_MATCH, false, STANDBY_MATCH|HAPPY_BOX_MATCH, true},
- {STANDBY_MATCH, false, STANDBY_MATCH|PENALTY_BOX_MATCH|HAPPY_BOX_MATCH, true},
- {STANDBY_MATCH, true, STANDBY_MATCH, true},
- {STANDBY_MATCH, true, STANDBY_MATCH|PENALTY_BOX_MATCH, true},
- {STANDBY_MATCH, true, STANDBY_MATCH|HAPPY_BOX_MATCH, true},
- {STANDBY_MATCH, true, STANDBY_MATCH|PENALTY_BOX_MATCH|HAPPY_BOX_MATCH, true},
+ // enabledRules, dataSaverEnabled, uidRules, blocked
+ {NO_MATCH, false, NO_MATCH, false},
+ {NO_MATCH, false, PENALTY_BOX_USER_MATCH, true},
+ {NO_MATCH, false, PENALTY_BOX_ADMIN_MATCH, true},
+ {NO_MATCH, false, PENALTY_BOX_USER_MATCH|PENALTY_BOX_ADMIN_MATCH, true},
+ {NO_MATCH, false, HAPPY_BOX_MATCH, false},
+ {NO_MATCH, false, PENALTY_BOX_USER_MATCH|HAPPY_BOX_MATCH, true},
+ {NO_MATCH, false, PENALTY_BOX_ADMIN_MATCH|HAPPY_BOX_MATCH, true},
+ {NO_MATCH, true, NO_MATCH, true},
+ {NO_MATCH, true, PENALTY_BOX_USER_MATCH, true},
+ {NO_MATCH, true, PENALTY_BOX_ADMIN_MATCH, true},
+ {NO_MATCH, true, PENALTY_BOX_USER_MATCH|PENALTY_BOX_ADMIN_MATCH, true},
+ {NO_MATCH, true, HAPPY_BOX_MATCH, false},
+ {NO_MATCH, true, PENALTY_BOX_USER_MATCH|HAPPY_BOX_MATCH, true},
+ {NO_MATCH, true, PENALTY_BOX_ADMIN_MATCH|HAPPY_BOX_MATCH, true},
+ {STANDBY_MATCH, false, STANDBY_MATCH, true},
+ {STANDBY_MATCH, false, STANDBY_MATCH|PENALTY_BOX_USER_MATCH, true},
+ {STANDBY_MATCH, false, STANDBY_MATCH|PENALTY_BOX_ADMIN_MATCH, true},
+ {STANDBY_MATCH, false, STANDBY_MATCH|HAPPY_BOX_MATCH, true},
+ {STANDBY_MATCH, false, STANDBY_MATCH|PENALTY_BOX_USER_MATCH|HAPPY_BOX_MATCH, true},
+ {STANDBY_MATCH, false, STANDBY_MATCH|PENALTY_BOX_ADMIN_MATCH|HAPPY_BOX_MATCH, true},
+ {STANDBY_MATCH, true, STANDBY_MATCH, true},
+ {STANDBY_MATCH, true, STANDBY_MATCH|PENALTY_BOX_USER_MATCH, true},
+ {STANDBY_MATCH, true, STANDBY_MATCH|PENALTY_BOX_ADMIN_MATCH, true},
+ {STANDBY_MATCH, true, STANDBY_MATCH|HAPPY_BOX_MATCH, true},
+ {STANDBY_MATCH, true, STANDBY_MATCH|PENALTY_BOX_USER_MATCH|HAPPY_BOX_MATCH, true},
+ {STANDBY_MATCH, true, STANDBY_MATCH|PENALTY_BOX_ADMIN_MATCH|HAPPY_BOX_MATCH, true},
// clang-format on
};
diff --git a/OWNERS_core_networking b/OWNERS_core_networking
index 83f798a..6d8ed4a 100644
--- a/OWNERS_core_networking
+++ b/OWNERS_core_networking
@@ -1,10 +1,6 @@
-chiachangwang@google.com
-cken@google.com
jchalard@google.com
junyulai@google.com
-lifr@google.com
lorenzo@google.com
-markchien@google.com
martinwu@google.com
maze@google.com
motomuman@google.com
@@ -12,7 +8,5 @@
prohr@google.com
reminv@google.com
satk@google.com
-waynema@google.com
xiaom@google.com
-yumike@google.com
yuyanghuang@google.com
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index 4bae221..304a6ed 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -113,6 +113,7 @@
prebuilts: [
"current_sdkinfo",
"netbpfload.mainline.rc",
+ "netbpfload.35rc",
"ot-daemon.init.34rc",
],
manifest: "manifest.json",
diff --git a/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java b/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java
index f696885..120b871 100644
--- a/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java
+++ b/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java
@@ -74,7 +74,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.BeforeClass;
import java.io.FileDescriptor;
import java.net.Inet4Address;
@@ -144,7 +143,7 @@
private static final Context sContext =
InstrumentationRegistry.getInstrumentation().getContext();
- private static final EthernetManager sEm = sContext.getSystemService(EthernetManager.class);
+ protected static final EthernetManager sEm = sContext.getSystemService(EthernetManager.class);
private static final TetheringManager sTm = sContext.getSystemService(TetheringManager.class);
private static final PackageManager sPackageManager = sContext.getPackageManager();
private static final CtsNetUtils sCtsNetUtils = new CtsNetUtils(sContext);
@@ -168,34 +167,6 @@
return sContext;
}
- @BeforeClass
- public static void setUpOnce() throws Exception {
- // The first test case may experience tethering restart with IP conflict handling.
- // Tethering would cache the last upstreams so that the next enabled tethering avoids
- // picking up the address that is in conflict with the upstreams. To protect subsequent
- // tests, turn tethering on and off before running them.
- MyTetheringEventCallback callback = null;
- TestNetworkInterface testIface = null;
- assumeTrue(sEm != null);
- try {
- // If the physical ethernet interface is available, do nothing.
- if (isInterfaceForTetheringAvailable()) return;
-
- testIface = createTestInterface();
- setIncludeTestInterfaces(true);
-
- callback = enableEthernetTethering(testIface.getInterfaceName(), null);
- callback.awaitUpstreamChanged(true /* throwTimeoutException */);
- } catch (TimeoutException e) {
- Log.d(TAG, "WARNNING " + e);
- } finally {
- maybeCloseTestInterface(testIface);
- maybeUnregisterTetheringEventCallback(callback);
-
- setIncludeTestInterfaces(false);
- }
- }
-
@Before
public void setUp() throws Exception {
mHandlerThread = new HandlerThread(getClass().getSimpleName());
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 4949eaa..c54d1b4 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -59,6 +59,7 @@
import com.android.testutils.NetworkStackModuleTest;
import com.android.testutils.TapPacketReader;
+import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -150,6 +151,35 @@
(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04 /* Address: 1.2.3.4 */
};
+ /** Enable/disable tethering once before running the tests. */
+ @BeforeClass
+ public static void setUpOnce() throws Exception {
+ // The first test case may experience tethering restart with IP conflict handling.
+ // Tethering would cache the last upstreams so that the next enabled tethering avoids
+ // picking up the address that is in conflict with the upstreams. To protect subsequent
+ // tests, turn tethering on and off before running them.
+ MyTetheringEventCallback callback = null;
+ TestNetworkInterface testIface = null;
+ assumeTrue(sEm != null);
+ try {
+ // If the physical ethernet interface is available, do nothing.
+ if (isInterfaceForTetheringAvailable()) return;
+
+ testIface = createTestInterface();
+ setIncludeTestInterfaces(true);
+
+ callback = enableEthernetTethering(testIface.getInterfaceName(), null);
+ callback.awaitUpstreamChanged(true /* throwTimeoutException */);
+ } catch (TimeoutException e) {
+ Log.d(TAG, "WARNNING " + e);
+ } finally {
+ maybeCloseTestInterface(testIface);
+ maybeUnregisterTetheringEventCallback(callback);
+
+ setIncludeTestInterfaces(false);
+ }
+ }
+
@Test
public void testVirtualEthernetAlreadyExists() throws Exception {
// This test requires manipulating packets. Skip if there is a physical Ethernet connected.
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index dfc7699..c3acaad 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -407,6 +407,9 @@
BpfConfig enabledRules = getConfig(UID_RULES_CONFIGURATION_KEY);
+ // BACKGROUND match does not apply to loopback traffic
+ if (skb->ifindex == 1) enabledRules &= ~BACKGROUND_MATCH;
+
UidOwnerValue* uidEntry = bpf_uid_owner_map_lookup_elem(&uid);
uint32_t uidRules = uidEntry ? uidEntry->rule : 0;
uint32_t allowed_iif = uidEntry ? uidEntry->iif : 0;
@@ -644,7 +647,8 @@
(struct __sk_buff* skb) {
uint32_t sock_uid = bpf_get_socket_uid(skb);
UidOwnerValue* denylistMatch = bpf_uid_owner_map_lookup_elem(&sock_uid);
- if (denylistMatch) return denylistMatch->rule & PENALTY_BOX_MATCH ? BPF_MATCH : BPF_NOMATCH;
+ uint32_t penalty_box = PENALTY_BOX_USER_MATCH | PENALTY_BOX_ADMIN_MATCH;
+ if (denylistMatch) return denylistMatch->rule & penalty_box ? BPF_MATCH : BPF_NOMATCH;
return BPF_NOMATCH;
}
diff --git a/bpf_progs/netd.h b/bpf_progs/netd.h
index 098147f..8a56b4a 100644
--- a/bpf_progs/netd.h
+++ b/bpf_progs/netd.h
@@ -181,7 +181,7 @@
enum UidOwnerMatchType : uint32_t {
NO_MATCH = 0,
HAPPY_BOX_MATCH = (1 << 0),
- PENALTY_BOX_MATCH = (1 << 1),
+ PENALTY_BOX_USER_MATCH = (1 << 1),
DOZABLE_MATCH = (1 << 2),
STANDBY_MATCH = (1 << 3),
POWERSAVE_MATCH = (1 << 4),
@@ -192,7 +192,8 @@
OEM_DENY_1_MATCH = (1 << 9),
OEM_DENY_2_MATCH = (1 << 10),
OEM_DENY_3_MATCH = (1 << 11),
- BACKGROUND_MATCH = (1 << 12)
+ BACKGROUND_MATCH = (1 << 12),
+ PENALTY_BOX_ADMIN_MATCH = (1 << 13),
};
// LINT.ThenChange(../framework/src/android/net/BpfNetMapsConstants.java)
diff --git a/common/FlaggedApi.bp b/common/FlaggedApi.bp
index 56625c5..21be1d3 100644
--- a/common/FlaggedApi.bp
+++ b/common/FlaggedApi.bp
@@ -17,7 +17,7 @@
aconfig_declarations {
name: "com.android.net.flags-aconfig",
package: "com.android.net.flags",
- container: "system",
+ container: "com.android.tethering",
srcs: ["flags.aconfig"],
visibility: ["//packages/modules/Connectivity:__subpackages__"],
}
@@ -25,7 +25,7 @@
aconfig_declarations {
name: "com.android.net.thread.flags-aconfig",
package: "com.android.net.thread.flags",
- container: "system",
+ container: "com.android.tethering",
srcs: ["thread_flags.aconfig"],
visibility: ["//packages/modules/Connectivity:__subpackages__"],
}
@@ -33,7 +33,7 @@
aconfig_declarations {
name: "nearby_flags",
package: "com.android.nearby.flags",
- container: "system",
+ container: "com.android.tethering",
srcs: ["nearby_flags.aconfig"],
visibility: ["//packages/modules/Connectivity:__subpackages__"],
}
diff --git a/common/flags.aconfig b/common/flags.aconfig
index 30931df..40e6cd8 100644
--- a/common/flags.aconfig
+++ b/common/flags.aconfig
@@ -1,5 +1,5 @@
package: "com.android.net.flags"
-container: "system"
+container: "com.android.tethering"
# This file contains aconfig flags for FlaggedAPI annotations
# Flags used from platform code must be in under frameworks
@@ -83,3 +83,11 @@
description: "Flag for API to register nsd offload engine"
bug: "301713539"
}
+
+flag {
+ name: "metered_network_firewall_chains"
+ is_exported: true
+ namespace: "android_core_networking"
+ description: "Flag for metered network firewall chain API"
+ bug: "332628891"
+}
diff --git a/common/nearby_flags.aconfig b/common/nearby_flags.aconfig
index b733849..55a865b 100644
--- a/common/nearby_flags.aconfig
+++ b/common/nearby_flags.aconfig
@@ -1,5 +1,5 @@
package: "com.android.nearby.flags"
-container: "system"
+container: "com.android.tethering"
flag {
name: "powered_off_finding"
diff --git a/common/thread_flags.aconfig b/common/thread_flags.aconfig
index 43fc147..43acd1b 100644
--- a/common/thread_flags.aconfig
+++ b/common/thread_flags.aconfig
@@ -1,5 +1,5 @@
package: "com.android.net.thread.flags"
-container: "system"
+container: "com.android.tethering"
flag {
name: "thread_enabled"
diff --git a/framework-t/src/android/net/INetworkStatsService.aidl b/framework-t/src/android/net/INetworkStatsService.aidl
index c86f7fd..7f0c1fe 100644
--- a/framework-t/src/android/net/INetworkStatsService.aidl
+++ b/framework-t/src/android/net/INetworkStatsService.aidl
@@ -101,4 +101,7 @@
* Note that invocation of any interface will be sent to all providers.
*/
void setStatsProviderWarningAndLimitAsync(String iface, long warning, long limit);
+
+ /** Clear TrafficStats rate-limit caches. */
+ void clearTrafficStatsRateLimitCaches();
}
diff --git a/framework-t/src/android/net/TrafficStats.java b/framework-t/src/android/net/TrafficStats.java
index a69b38d..77c8001 100644
--- a/framework-t/src/android/net/TrafficStats.java
+++ b/framework-t/src/android/net/TrafficStats.java
@@ -19,6 +19,7 @@
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -692,6 +693,27 @@
return UNSUPPORTED;
}
+ /** Clear TrafficStats rate-limit caches.
+ *
+ * This is mainly for {@link com.android.server.net.NetworkStatsService} to
+ * clear rate-limit cache to avoid caching for TrafficStats API results.
+ * Tests might get stale values after generating network traffic, which
+ * generally need to wait for cache expiry to get updated values.
+ *
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK,
+ android.Manifest.permission.NETWORK_SETTINGS})
+ public static void clearRateLimitCaches() {
+ try {
+ getStatsService().clearTrafficStatsRateLimitCaches();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Return the number of packets transmitted on the specified interface since the interface
* was created. Statistics are measured at the network layer, so both TCP and
diff --git a/framework-t/src/android/net/nsd/NsdManager.java b/framework-t/src/android/net/nsd/NsdManager.java
index 1001423..48d40e6 100644
--- a/framework-t/src/android/net/nsd/NsdManager.java
+++ b/framework-t/src/android/net/nsd/NsdManager.java
@@ -389,6 +389,7 @@
}
private static final int FIRST_LISTENER_KEY = 1;
+ private static final int DNSSEC_PROTOCOL = 3;
private final INsdServiceConnector mService;
private final Context mContext;
@@ -1754,45 +1755,132 @@
}
}
+ private enum ServiceValidationType {
+ NO_SERVICE,
+ HAS_SERVICE, // A service with a positive port
+ HAS_SERVICE_ZERO_PORT, // A service with a zero port
+ }
+
+ private enum HostValidationType {
+ DEFAULT_HOST, // No host is specified so the default host will be used
+ CUSTOM_HOST, // A custom host with addresses is specified
+ CUSTOM_HOST_NO_ADDRESS, // A custom host without address is specified
+ }
+
+ private enum PublicKeyValidationType {
+ NO_KEY,
+ HAS_KEY,
+ }
+
+ /**
+ * Check if the service is valid for registration and classify it as one of {@link
+ * ServiceValidationType}.
+ */
+ private static ServiceValidationType validateService(NsdServiceInfo serviceInfo) {
+ final boolean hasServiceName = !TextUtils.isEmpty(serviceInfo.getServiceName());
+ final boolean hasServiceType = !TextUtils.isEmpty(serviceInfo.getServiceType());
+ if (!hasServiceName && !hasServiceType && serviceInfo.getPort() == 0) {
+ return ServiceValidationType.NO_SERVICE;
+ }
+ if (hasServiceName && hasServiceType) {
+ if (serviceInfo.getPort() < 0) {
+ throw new IllegalArgumentException("Invalid port");
+ }
+ if (serviceInfo.getPort() == 0) {
+ return ServiceValidationType.HAS_SERVICE_ZERO_PORT;
+ }
+ return ServiceValidationType.HAS_SERVICE;
+ }
+ throw new IllegalArgumentException("The service name or the service type is missing");
+ }
+
+ /**
+ * Check if the host is valid for registration and classify it as one of {@link
+ * HostValidationType}.
+ */
+ private static HostValidationType validateHost(NsdServiceInfo serviceInfo) {
+ final boolean hasHostname = !TextUtils.isEmpty(serviceInfo.getHostname());
+ final boolean hasHostAddresses = !CollectionUtils.isEmpty(serviceInfo.getHostAddresses());
+ if (!hasHostname) {
+ // Keep compatible with the legacy behavior: It's allowed to set host
+ // addresses for a service registration although the host addresses
+ // won't be registered. To register the addresses for a host, the
+ // hostname must be specified.
+ return HostValidationType.DEFAULT_HOST;
+ }
+ if (!hasHostAddresses) {
+ return HostValidationType.CUSTOM_HOST_NO_ADDRESS;
+ }
+ return HostValidationType.CUSTOM_HOST;
+ }
+
+ /**
+ * Check if the public key is valid for registration and classify it as one of {@link
+ * PublicKeyValidationType}.
+ *
+ * <p>For simplicity, it only checks if the protocol is DNSSEC and the RDATA is not fewer than 4
+ * bytes. See RFC 3445 Section 3.
+ */
+ private static PublicKeyValidationType validatePublicKey(NsdServiceInfo serviceInfo) {
+ byte[] publicKey = serviceInfo.getPublicKey();
+ if (publicKey == null) {
+ return PublicKeyValidationType.NO_KEY;
+ }
+ if (publicKey.length < 4) {
+ throw new IllegalArgumentException("The public key should be at least 4 bytes long");
+ }
+ int protocol = publicKey[2];
+ if (protocol == DNSSEC_PROTOCOL) {
+ return PublicKeyValidationType.HAS_KEY;
+ }
+ throw new IllegalArgumentException(
+ "The public key's protocol ("
+ + protocol
+ + ") is invalid. It should be DNSSEC_PROTOCOL (3)");
+ }
+
/**
* Check if the {@link NsdServiceInfo} is valid for registration.
*
- * The following can be registered:
- * - A service with an optional host.
- * - A hostname with addresses.
+ * <p>Firstly, check if service, host and public key are all valid respectively. Then check if
+ * the combination of service, host and public key is valid.
*
- * Note that:
- * - When registering a service, the service name, service type and port must be specified. If
- * hostname is specified, the host addresses can optionally be specified.
- * - When registering a host without a service, the addresses must be specified.
+ * <p>If the {@code serviceInfo} is invalid, throw an {@link IllegalArgumentException}
+ * describing the reason.
+ *
+ * <p>There are the invalid combinations of service, host and public key:
+ *
+ * <ul>
+ * <li>Neither service nor host is specified.
+ * <li>No public key is specified and the service has a zero port.
+ * <li>The registration only contains the hostname but addresses are missing.
+ * </ul>
+ *
+ * <p>Keys are used to reserve hostnames or service names while the service/host is temporarily
+ * inactive, so registrations with a key and just a hostname or a service name are acceptable.
*
* @hide
*/
public static void checkServiceInfoForRegistration(NsdServiceInfo serviceInfo) {
Objects.requireNonNull(serviceInfo, "NsdServiceInfo cannot be null");
- boolean hasServiceName = !TextUtils.isEmpty(serviceInfo.getServiceName());
- boolean hasServiceType = !TextUtils.isEmpty(serviceInfo.getServiceType());
- boolean hasHostname = !TextUtils.isEmpty(serviceInfo.getHostname());
- boolean hasHostAddresses = !CollectionUtils.isEmpty(serviceInfo.getHostAddresses());
- if (serviceInfo.getPort() < 0) {
- throw new IllegalArgumentException("Invalid port");
+ final ServiceValidationType serviceValidation = validateService(serviceInfo);
+ final HostValidationType hostValidation = validateHost(serviceInfo);
+ final PublicKeyValidationType publicKeyValidation = validatePublicKey(serviceInfo);
+
+ if (serviceValidation == ServiceValidationType.NO_SERVICE
+ && hostValidation == HostValidationType.DEFAULT_HOST) {
+ throw new IllegalArgumentException("Nothing to register");
}
-
- if (hasServiceType || hasServiceName || (serviceInfo.getPort() > 0)) {
- if (!(hasServiceType && hasServiceName && (serviceInfo.getPort() > 0))) {
- throw new IllegalArgumentException(
- "The service type, service name or port is missing");
+ if (publicKeyValidation == PublicKeyValidationType.NO_KEY) {
+ if (serviceValidation == ServiceValidationType.HAS_SERVICE_ZERO_PORT) {
+ throw new IllegalArgumentException("The port is missing");
}
- }
-
- if (!hasServiceType && !hasHostname) {
- throw new IllegalArgumentException("No service or host specified in NsdServiceInfo");
- }
-
- if (!hasServiceType && hasHostname && !hasHostAddresses) {
- // TODO: b/317946010 - This may be allowed when it supports registering KEY RR.
- throw new IllegalArgumentException("No host addresses specified in NsdServiceInfo");
+ if (serviceValidation == ServiceValidationType.NO_SERVICE
+ && hostValidation == HostValidationType.CUSTOM_HOST_NO_ADDRESS) {
+ throw new IllegalArgumentException(
+ "The host addresses must be specified unless there is a service");
+ }
}
}
}
diff --git a/framework-t/src/android/net/nsd/NsdServiceInfo.java b/framework-t/src/android/net/nsd/NsdServiceInfo.java
index 9491a9c..2f675a9 100644
--- a/framework-t/src/android/net/nsd/NsdServiceInfo.java
+++ b/framework-t/src/android/net/nsd/NsdServiceInfo.java
@@ -37,6 +37,7 @@
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -69,6 +70,9 @@
private int mPort;
@Nullable
+ private byte[] mPublicKey;
+
+ @Nullable
private Network mNetwork;
private int mInterfaceIndex;
@@ -220,6 +224,40 @@
}
/**
+ * Set the public key RDATA to be advertised in a KEY RR (RFC 2535).
+ *
+ * <p>This is the public key of the key pair used for signing a DNS message (e.g. SRP). Clients
+ * typically don't need this information, but the KEY RR is usually published to claim the use
+ * of the DNS name so that another mDNS advertiser can't take over the ownership during a
+ * temporary power down of the original host device.
+ *
+ * <p>When the public key is set to non-null, exactly one KEY RR will be advertised for each of
+ * the service and host name if they are not null.
+ *
+ * @hide // For Thread only
+ */
+ public void setPublicKey(@Nullable byte[] publicKey) {
+ if (publicKey == null) {
+ mPublicKey = null;
+ return;
+ }
+ mPublicKey = Arrays.copyOf(publicKey, publicKey.length);
+ }
+
+ /**
+ * Get the public key RDATA in the KEY RR (RFC 2535) or {@code null} if no KEY RR exists.
+ *
+ * @hide // For Thread only
+ */
+ @Nullable
+ public byte[] getPublicKey() {
+ if (mPublicKey == null) {
+ return null;
+ }
+ return Arrays.copyOf(mPublicKey, mPublicKey.length);
+ }
+
+ /**
* Unpack txt information from a base-64 encoded byte array.
*
* @param txtRecordsRawBytes The raw base64 encoded byte array.
@@ -622,6 +660,7 @@
}
dest.writeString(mHostname);
dest.writeLong(mExpirationTime != null ? mExpirationTime.getEpochSecond() : -1);
+ dest.writeByteArray(mPublicKey);
}
/** Implement the Parcelable interface */
@@ -654,6 +693,7 @@
info.mHostname = in.readString();
final long seconds = in.readLong();
info.setExpirationTime(seconds < 0 ? null : Instant.ofEpochSecond(seconds));
+ info.mPublicKey = in.createByteArray();
return info;
}
diff --git a/framework/api/module-lib-current.txt b/framework/api/module-lib-current.txt
index 026d8a9..b2aafa0 100644
--- a/framework/api/module-lib-current.txt
+++ b/framework/api/module-lib-current.txt
@@ -56,6 +56,9 @@
field @FlaggedApi("com.android.net.flags.basic_background_restrictions_enabled") public static final int FIREWALL_CHAIN_BACKGROUND = 6; // 0x6
field public static final int FIREWALL_CHAIN_DOZABLE = 1; // 0x1
field public static final int FIREWALL_CHAIN_LOW_POWER_STANDBY = 5; // 0x5
+ field @FlaggedApi("com.android.net.flags.metered_network_firewall_chains") public static final int FIREWALL_CHAIN_METERED_ALLOW = 10; // 0xa
+ field @FlaggedApi("com.android.net.flags.metered_network_firewall_chains") public static final int FIREWALL_CHAIN_METERED_DENY_ADMIN = 12; // 0xc
+ field @FlaggedApi("com.android.net.flags.metered_network_firewall_chains") public static final int FIREWALL_CHAIN_METERED_DENY_USER = 11; // 0xb
field public static final int FIREWALL_CHAIN_OEM_DENY_1 = 7; // 0x7
field public static final int FIREWALL_CHAIN_OEM_DENY_2 = 8; // 0x8
field public static final int FIREWALL_CHAIN_OEM_DENY_3 = 9; // 0x9
diff --git a/framework/src/android/net/BpfNetMapsConstants.java b/framework/src/android/net/BpfNetMapsConstants.java
index 5d0fe73..f3773de 100644
--- a/framework/src/android/net/BpfNetMapsConstants.java
+++ b/framework/src/android/net/BpfNetMapsConstants.java
@@ -19,6 +19,9 @@
import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_1;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_2;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_3;
@@ -67,7 +70,7 @@
// LINT.IfChange(match_type)
public static final long NO_MATCH = 0;
public static final long HAPPY_BOX_MATCH = (1 << 0);
- public static final long PENALTY_BOX_MATCH = (1 << 1);
+ public static final long PENALTY_BOX_USER_MATCH = (1 << 1);
public static final long DOZABLE_MATCH = (1 << 2);
public static final long STANDBY_MATCH = (1 << 3);
public static final long POWERSAVE_MATCH = (1 << 4);
@@ -79,10 +82,11 @@
public static final long OEM_DENY_2_MATCH = (1 << 10);
public static final long OEM_DENY_3_MATCH = (1 << 11);
public static final long BACKGROUND_MATCH = (1 << 12);
+ public static final long PENALTY_BOX_ADMIN_MATCH = (1 << 13);
public static final List<Pair<Long, String>> MATCH_LIST = Arrays.asList(
Pair.create(HAPPY_BOX_MATCH, "HAPPY_BOX_MATCH"),
- Pair.create(PENALTY_BOX_MATCH, "PENALTY_BOX_MATCH"),
+ Pair.create(PENALTY_BOX_USER_MATCH, "PENALTY_BOX_USER_MATCH"),
Pair.create(DOZABLE_MATCH, "DOZABLE_MATCH"),
Pair.create(STANDBY_MATCH, "STANDBY_MATCH"),
Pair.create(POWERSAVE_MATCH, "POWERSAVE_MATCH"),
@@ -93,11 +97,13 @@
Pair.create(OEM_DENY_1_MATCH, "OEM_DENY_1_MATCH"),
Pair.create(OEM_DENY_2_MATCH, "OEM_DENY_2_MATCH"),
Pair.create(OEM_DENY_3_MATCH, "OEM_DENY_3_MATCH"),
- Pair.create(BACKGROUND_MATCH, "BACKGROUND_MATCH")
+ Pair.create(BACKGROUND_MATCH, "BACKGROUND_MATCH"),
+ Pair.create(PENALTY_BOX_ADMIN_MATCH, "PENALTY_BOX_ADMIN_MATCH")
);
/**
- * List of all firewall allow chains.
+ * List of all firewall allow chains that are applied to all networks regardless of meteredness
+ * See {@link #METERED_ALLOW_CHAINS} for allow chains that are only applied to metered networks.
*
* Allow chains mean the firewall denies all uids by default, uids must be explicitly allowed.
*/
@@ -110,7 +116,8 @@
);
/**
- * List of all firewall deny chains.
+ * List of all firewall deny chains that are applied to all networks regardless of meteredness
+ * See {@link #METERED_DENY_CHAINS} for deny chains that are only applied to metered networks.
*
* Deny chains mean the firewall allows all uids by default, uids must be explicitly denied.
*/
@@ -120,5 +127,24 @@
FIREWALL_CHAIN_OEM_DENY_2,
FIREWALL_CHAIN_OEM_DENY_3
);
+
+ /**
+ * List of all firewall allow chains that are only applied to metered networks.
+ * See {@link #ALLOW_CHAINS} for allow chains that are applied to all networks regardless of
+ * meteredness.
+ */
+ public static final List<Integer> METERED_ALLOW_CHAINS = List.of(
+ FIREWALL_CHAIN_METERED_ALLOW
+ );
+
+ /**
+ * List of all firewall deny chains that are only applied to metered networks.
+ * See {@link #DENY_CHAINS} for deny chains that are applied to all networks regardless of
+ * meteredness.
+ */
+ public static final List<Integer> METERED_DENY_CHAINS = List.of(
+ FIREWALL_CHAIN_METERED_DENY_USER,
+ FIREWALL_CHAIN_METERED_DENY_ADMIN
+ );
// LINT.ThenChange(../../../../bpf_progs/netd.h)
}
diff --git a/framework/src/android/net/BpfNetMapsUtils.java b/framework/src/android/net/BpfNetMapsUtils.java
index 19ecafb..4e01fee 100644
--- a/framework/src/android/net/BpfNetMapsUtils.java
+++ b/framework/src/android/net/BpfNetMapsUtils.java
@@ -25,11 +25,14 @@
import static android.net.BpfNetMapsConstants.HAPPY_BOX_MATCH;
import static android.net.BpfNetMapsConstants.LOW_POWER_STANDBY_MATCH;
import static android.net.BpfNetMapsConstants.MATCH_LIST;
+import static android.net.BpfNetMapsConstants.METERED_ALLOW_CHAINS;
+import static android.net.BpfNetMapsConstants.METERED_DENY_CHAINS;
import static android.net.BpfNetMapsConstants.NO_MATCH;
import static android.net.BpfNetMapsConstants.OEM_DENY_1_MATCH;
import static android.net.BpfNetMapsConstants.OEM_DENY_2_MATCH;
import static android.net.BpfNetMapsConstants.OEM_DENY_3_MATCH;
-import static android.net.BpfNetMapsConstants.PENALTY_BOX_MATCH;
+import static android.net.BpfNetMapsConstants.PENALTY_BOX_ADMIN_MATCH;
+import static android.net.BpfNetMapsConstants.PENALTY_BOX_USER_MATCH;
import static android.net.BpfNetMapsConstants.POWERSAVE_MATCH;
import static android.net.BpfNetMapsConstants.RESTRICTED_MATCH;
import static android.net.BpfNetMapsConstants.STANDBY_MATCH;
@@ -37,6 +40,9 @@
import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_1;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_2;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_3;
@@ -47,12 +53,15 @@
import static android.net.ConnectivityManager.FIREWALL_RULE_DENY;
import static android.system.OsConstants.EINVAL;
+import android.os.Build;
import android.os.Process;
import android.os.ServiceSpecificException;
import android.system.ErrnoException;
import android.system.Os;
import android.util.Pair;
+import androidx.annotation.RequiresApi;
+
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.IBpfMap;
import com.android.net.module.util.Struct;
@@ -70,6 +79,8 @@
// Note that this class should be put into bootclasspath instead of static libraries.
// Because modules could have different copies of this class if this is statically linked,
// which would be problematic if the definitions in these modules are not synchronized.
+// Note that NetworkStack can not use this before U due to b/326143935
+@RequiresApi(Build.VERSION_CODES.TIRAMISU)
public class BpfNetMapsUtils {
// Bitmaps for calculating whether a given uid is blocked by firewall chains.
private static final long sMaskDropIfSet;
@@ -117,6 +128,12 @@
return OEM_DENY_2_MATCH;
case FIREWALL_CHAIN_OEM_DENY_3:
return OEM_DENY_3_MATCH;
+ case FIREWALL_CHAIN_METERED_ALLOW:
+ return HAPPY_BOX_MATCH;
+ case FIREWALL_CHAIN_METERED_DENY_USER:
+ return PENALTY_BOX_USER_MATCH;
+ case FIREWALL_CHAIN_METERED_DENY_ADMIN:
+ return PENALTY_BOX_ADMIN_MATCH;
default:
throw new ServiceSpecificException(EINVAL, "Invalid firewall chain: " + chain);
}
@@ -129,9 +146,9 @@
* DENYLIST means the firewall allows all by default, uids must be explicitly denied
*/
public static boolean isFirewallAllowList(final int chain) {
- if (ALLOW_CHAINS.contains(chain)) {
+ if (ALLOW_CHAINS.contains(chain) || METERED_ALLOW_CHAINS.contains(chain)) {
return true;
- } else if (DENY_CHAINS.contains(chain)) {
+ } else if (DENY_CHAINS.contains(chain) || METERED_DENY_CHAINS.contains(chain)) {
return false;
}
throw new ServiceSpecificException(EINVAL, "Invalid firewall chain: " + chain);
@@ -264,7 +281,7 @@
}
if (!isNetworkMetered) return false;
- if ((uidMatch & PENALTY_BOX_MATCH) != 0) return true;
+ if ((uidMatch & (PENALTY_BOX_USER_MATCH | PENALTY_BOX_ADMIN_MATCH)) != 0) return true;
if ((uidMatch & HAPPY_BOX_MATCH) != 0) return false;
return getDataSaverEnabled(dataSaverEnabledMap);
}
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index b1e636d..7823258 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -128,6 +128,8 @@
"com.android.net.flags.support_is_uid_networking_blocked";
static final String BASIC_BACKGROUND_RESTRICTIONS_ENABLED =
"com.android.net.flags.basic_background_restrictions_enabled";
+ static final String METERED_NETWORK_FIREWALL_CHAINS =
+ "com.android.net.flags.metered_network_firewall_chains";
}
/**
@@ -1068,6 +1070,61 @@
@SystemApi(client = MODULE_LIBRARIES)
public static final int FIREWALL_CHAIN_OEM_DENY_3 = 9;
+ /**
+ * Firewall chain for allow list on metered networks
+ *
+ * UIDs added to this chain have access to metered networks, unless they're also in one of the
+ * denylist, {@link #FIREWALL_CHAIN_METERED_DENY_USER},
+ * {@link #FIREWALL_CHAIN_METERED_DENY_ADMIN}
+ *
+ * Note that this chain is used from a separate bpf program that is triggered by iptables and
+ * can not be controlled by {@link ConnectivityManager#setFirewallChainEnabled}.
+ *
+ * @hide
+ */
+ // TODO: Merge this chain with data saver and support setFirewallChainEnabled
+ @FlaggedApi(Flags.METERED_NETWORK_FIREWALL_CHAINS)
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static final int FIREWALL_CHAIN_METERED_ALLOW = 10;
+
+ /**
+ * Firewall chain for user-set restrictions on metered networks
+ *
+ * UIDs added to this chain do not have access to metered networks.
+ * UIDs should be added to this chain based on user settings.
+ * To restrict metered network based on admin configuration (e.g. enterprise policies),
+ * {@link #FIREWALL_CHAIN_METERED_DENY_ADMIN} should be used.
+ * This chain corresponds to {@link #BLOCKED_METERED_REASON_USER_RESTRICTED}
+ *
+ * Note that this chain is used from a separate bpf program that is triggered by iptables and
+ * can not be controlled by {@link ConnectivityManager#setFirewallChainEnabled}.
+ *
+ * @hide
+ */
+ // TODO: Support setFirewallChainEnabled to control this chain
+ @FlaggedApi(Flags.METERED_NETWORK_FIREWALL_CHAINS)
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static final int FIREWALL_CHAIN_METERED_DENY_USER = 11;
+
+ /**
+ * Firewall chain for admin-set restrictions on metered networks
+ *
+ * UIDs added to this chain do not have access to metered networks.
+ * UIDs should be added to this chain based on admin configuration (e.g. enterprise policies).
+ * To restrict metered network based on user settings, {@link #FIREWALL_CHAIN_METERED_DENY_USER}
+ * should be used.
+ * This chain corresponds to {@link #BLOCKED_METERED_REASON_ADMIN_DISABLED}
+ *
+ * Note that this chain is used from a separate bpf program that is triggered by iptables and
+ * can not be controlled by {@link ConnectivityManager#setFirewallChainEnabled}.
+ *
+ * @hide
+ */
+ // TODO: Support setFirewallChainEnabled to control this chain
+ @FlaggedApi(Flags.METERED_NETWORK_FIREWALL_CHAINS)
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static final int FIREWALL_CHAIN_METERED_DENY_ADMIN = 12;
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = false, prefix = "FIREWALL_CHAIN_", value = {
@@ -1079,7 +1136,10 @@
FIREWALL_CHAIN_BACKGROUND,
FIREWALL_CHAIN_OEM_DENY_1,
FIREWALL_CHAIN_OEM_DENY_2,
- FIREWALL_CHAIN_OEM_DENY_3
+ FIREWALL_CHAIN_OEM_DENY_3,
+ FIREWALL_CHAIN_METERED_ALLOW,
+ FIREWALL_CHAIN_METERED_DENY_USER,
+ FIREWALL_CHAIN_METERED_DENY_ADMIN
})
public @interface FirewallChain {}
@@ -6065,7 +6125,7 @@
})
public void addUidToMeteredNetworkAllowList(final int uid) {
try {
- mService.updateMeteredNetworkAllowList(uid, true /* add */);
+ mService.setUidFirewallRule(FIREWALL_CHAIN_METERED_ALLOW, uid, FIREWALL_RULE_ALLOW);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -6088,7 +6148,7 @@
})
public void removeUidFromMeteredNetworkAllowList(final int uid) {
try {
- mService.updateMeteredNetworkAllowList(uid, false /* remove */);
+ mService.setUidFirewallRule(FIREWALL_CHAIN_METERED_ALLOW, uid, FIREWALL_RULE_DENY);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -6098,10 +6158,17 @@
* Adds the specified UID to the list of UIDs that are not allowed to use background data on
* metered networks. Takes precedence over {@link #addUidToMeteredNetworkAllowList}.
*
+ * On V+, {@link #setUidFirewallRule} should be used with
+ * {@link #FIREWALL_CHAIN_METERED_DENY_USER} or {@link #FIREWALL_CHAIN_METERED_DENY_ADMIN}
+ * based on the reason so that users can receive {@link #BLOCKED_METERED_REASON_USER_RESTRICTED}
+ * or {@link #BLOCKED_METERED_REASON_ADMIN_DISABLED}, respectively.
+ * This API always uses {@link #FIREWALL_CHAIN_METERED_DENY_USER}.
+ *
* @param uid uid of target app
* @throws IllegalStateException if updating deny list failed.
* @hide
*/
+ // TODO(b/332649177): Deprecate this API after V
@SystemApi(client = MODULE_LIBRARIES)
@RequiresPermission(anyOf = {
android.Manifest.permission.NETWORK_SETTINGS,
@@ -6110,7 +6177,7 @@
})
public void addUidToMeteredNetworkDenyList(final int uid) {
try {
- mService.updateMeteredNetworkDenyList(uid, true /* add */);
+ mService.setUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_USER, uid, FIREWALL_RULE_DENY);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -6121,10 +6188,17 @@
* networks if background data is not restricted. The deny list takes precedence over the
* allow list.
*
+ * On V+, {@link #setUidFirewallRule} should be used with
+ * {@link #FIREWALL_CHAIN_METERED_DENY_USER} or {@link #FIREWALL_CHAIN_METERED_DENY_ADMIN}
+ * based on the reason so that users can receive {@link #BLOCKED_METERED_REASON_USER_RESTRICTED}
+ * or {@link #BLOCKED_METERED_REASON_ADMIN_DISABLED}, respectively.
+ * This API always uses {@link #FIREWALL_CHAIN_METERED_DENY_USER}.
+ *
* @param uid uid of target app
* @throws IllegalStateException if updating deny list failed.
* @hide
*/
+ // TODO(b/332649177): Deprecate this API after V
@SystemApi(client = MODULE_LIBRARIES)
@RequiresPermission(anyOf = {
android.Manifest.permission.NETWORK_SETTINGS,
@@ -6133,7 +6207,7 @@
})
public void removeUidFromMeteredNetworkDenyList(final int uid) {
try {
- mService.updateMeteredNetworkDenyList(uid, false /* remove */);
+ mService.setUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_USER, uid, FIREWALL_RULE_ALLOW);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -6191,6 +6265,10 @@
/**
* Enables or disables the specified firewall chain.
*
+ * Note that metered firewall chains can not be controlled by this API.
+ * See {@link #FIREWALL_CHAIN_METERED_ALLOW}, {@link #FIREWALL_CHAIN_METERED_DENY_USER}, and
+ * {@link #FIREWALL_CHAIN_METERED_DENY_ADMIN} for more detail.
+ *
* @param chain target chain.
* @param enable whether the chain should be enabled.
* @throws UnsupportedOperationException if called on pre-T devices.
diff --git a/framework/src/android/net/IConnectivityManager.aidl b/framework/src/android/net/IConnectivityManager.aidl
index d3a02b9..55c7085 100644
--- a/framework/src/android/net/IConnectivityManager.aidl
+++ b/framework/src/android/net/IConnectivityManager.aidl
@@ -242,10 +242,6 @@
void setDataSaverEnabled(boolean enable);
- void updateMeteredNetworkAllowList(int uid, boolean add);
-
- void updateMeteredNetworkDenyList(int uid, boolean add);
-
void setUidFirewallRule(int chain, int uid, int rule);
int getUidFirewallRule(int chain, int uid);
diff --git a/netbpfload/Android.bp b/netbpfload/Android.bp
index c39b46c..f278695 100644
--- a/netbpfload/Android.bp
+++ b/netbpfload/Android.bp
@@ -75,3 +75,10 @@
filename: "netbpfload.33rc",
installable: false,
}
+
+prebuilt_etc {
+ name: "netbpfload.35rc",
+ src: "netbpfload.35rc",
+ filename: "netbpfload.35rc",
+ installable: false,
+}
diff --git a/netbpfload/NetBpfLoad.cpp b/netbpfload/NetBpfLoad.cpp
index 83bb98c..ccb6acb 100644
--- a/netbpfload/NetBpfLoad.cpp
+++ b/netbpfload/NetBpfLoad.cpp
@@ -51,23 +51,22 @@
#include "bpf/BpfUtils.h"
#include "loader.h"
-using android::base::EndsWith;
-using android::bpf::domain;
+namespace android {
+namespace bpf {
+
+using base::EndsWith;
using std::string;
-bool exists(const char* const path) {
+static bool exists(const char* const path) {
int v = access(path, F_OK);
- if (!v) {
- ALOGI("%s exists.", path);
- return true;
- }
+ if (!v) return true;
if (errno == ENOENT) return false;
ALOGE("FATAL: access(%s, F_OK) -> %d [%d:%s]", path, v, errno, strerror(errno));
abort(); // can only hit this if permissions (likely selinux) are screwed up
}
-const android::bpf::Location locations[] = {
+const Location locations[] = {
// S+ Tethering mainline module (network_stack): tether offload
{
.dir = "/apex/com.android.tethering/etc/bpf/",
@@ -97,7 +96,7 @@
},
};
-int loadAllElfObjects(const unsigned int bpfloader_ver, const android::bpf::Location& location) {
+static int loadAllElfObjects(const unsigned int bpfloader_ver, const Location& location) {
int retVal = 0;
DIR* dir;
struct dirent* ent;
@@ -111,12 +110,12 @@
progPath += s;
bool critical;
- int ret = android::bpf::loadProg(progPath.c_str(), &critical, bpfloader_ver, location);
+ int ret = loadProg(progPath.c_str(), &critical, bpfloader_ver, location);
if (ret) {
if (critical) retVal = ret;
ALOGE("Failed to load object: %s, ret: %s", progPath.c_str(), std::strerror(-ret));
} else {
- ALOGI("Loaded object: %s", progPath.c_str());
+ ALOGD("Loaded object: %s", progPath.c_str());
}
}
closedir(dir);
@@ -124,7 +123,7 @@
return retVal;
}
-int createSysFsBpfSubDir(const char* const prefix) {
+static int createSysFsBpfSubDir(const char* const prefix) {
if (*prefix) {
mode_t prevUmask = umask(0);
@@ -147,8 +146,8 @@
// Technically 'value' doesn't need to be newline terminated, but it's best
// to include a newline to match 'echo "value" > /proc/sys/...foo' behaviour,
// which is usually how kernel devs test the actual sysctl interfaces.
-int writeProcSysFile(const char *filename, const char *value) {
- android::base::unique_fd fd(open(filename, O_WRONLY | O_CLOEXEC));
+static int writeProcSysFile(const char *filename, const char *value) {
+ base::unique_fd fd(open(filename, O_WRONLY | O_CLOEXEC));
if (fd < 0) {
const int err = errno;
ALOGE("open('%s', O_WRONLY | O_CLOEXEC) -> %s", filename, strerror(err));
@@ -172,7 +171,7 @@
#define APEX_MOUNT_POINT "/apex/com.android.tethering"
const char * const platformBpfLoader = "/system/bin/bpfloader";
-int logTetheringApexVersion(void) {
+static int logTetheringApexVersion(void) {
char * found_blockdev = NULL;
FILE * f = NULL;
char buf[4096];
@@ -198,7 +197,7 @@
f = NULL;
if (!found_blockdev) return 2;
- ALOGD("Found Tethering Apex mounted from blockdev %s", found_blockdev);
+ ALOGV("Found Tethering Apex mounted from blockdev %s", found_blockdev);
f = fopen("/proc/mounts", "re");
if (!f) { free(found_blockdev); return 3; }
@@ -224,12 +223,12 @@
return 0;
}
-int main(int argc, char** argv, char * const envp[]) {
- (void)argc;
- android::base::InitLogging(argv, &android::base::KernelLogger);
+static bool isGSI() {
+ // From //system/gsid/libgsi.cpp IsGsiRunning()
+ return !access("/metadata/gsi/dsu/booted", F_OK);
+}
- ALOGI("NetBpfLoad '%s' starting...", argv[0]);
-
+static int doLoad(char** argv, char * const envp[]) {
const int device_api_level = android_get_device_api_level();
const bool isAtLeastT = (device_api_level >= __ANDROID_API_T__);
const bool isAtLeastU = (device_api_level >= __ANDROID_API_U__);
@@ -240,9 +239,9 @@
// first in U QPR2 beta~2
const bool has_platform_netbpfload_rc = exists("/system/etc/init/netbpfload.rc");
- ALOGI("NetBpfLoad api:%d/%d kver:%07x rc:%d%d",
- android_get_application_target_sdk_version(), device_api_level,
- android::bpf::kernelVersion(),
+ ALOGI("NetBpfLoad (%s) api:%d/%d kver:%07x (%s) rc:%d%d",
+ argv[0], android_get_application_target_sdk_version(), device_api_level,
+ kernelVersion(), describeArch(),
has_platform_bpfloader_rc, has_platform_netbpfload_rc);
if (!has_platform_bpfloader_rc && !has_platform_netbpfload_rc) {
@@ -262,27 +261,55 @@
return 1;
}
- if (isAtLeastT && !android::bpf::isAtLeastKernelVersion(4, 9, 0)) {
+ if (isAtLeastT && !isAtLeastKernelVersion(4, 9, 0)) {
ALOGE("Android T requires kernel 4.9.");
return 1;
}
- if (isAtLeastU && !android::bpf::isAtLeastKernelVersion(4, 14, 0)) {
+ if (isAtLeastU && !isAtLeastKernelVersion(4, 14, 0)) {
ALOGE("Android U requires kernel 4.14.");
return 1;
}
- if (isAtLeastV && !android::bpf::isAtLeastKernelVersion(4, 19, 0)) {
+ if (isAtLeastV && !isAtLeastKernelVersion(4, 19, 0)) {
ALOGE("Android V requires kernel 4.19.");
return 1;
}
- if (isAtLeastV && android::bpf::isX86() && !android::bpf::isKernel64Bit()) {
+ if (isAtLeastV && isX86() && !isKernel64Bit()) {
ALOGE("Android V requires X86 kernel to be 64-bit.");
return 1;
}
- if (android::bpf::isUserspace32bit() && android::bpf::isAtLeastKernelVersion(6, 2, 0)) {
+ if (isAtLeastV) {
+ bool bad = false;
+
+ if (!isLtsKernel()) {
+ ALOGW("Android V only supports LTS kernels.");
+ bad = true;
+ }
+
+#define REQUIRE(maj, min, sub) \
+ if (isKernelVersion(maj, min) && !isAtLeastKernelVersion(maj, min, sub)) { \
+ ALOGW("Android V requires %d.%d kernel to be %d.%d.%d+.", maj, min, maj, min, sub); \
+ bad = true; \
+ }
+
+ REQUIRE(4, 19, 236)
+ REQUIRE(5, 4, 186)
+ REQUIRE(5, 10, 199)
+ REQUIRE(5, 15, 136)
+ REQUIRE(6, 1, 57)
+ REQUIRE(6, 6, 0)
+
+#undef REQUIRE
+
+ if (bad && !isGSI()) {
+ ALOGE("Unsupported kernel version (%07x).", kernelVersion());
+ }
+ }
+
+ if (isUserspace32bit() && isAtLeastKernelVersion(6, 2, 0)) {
/* Android 14/U should only launch on 64-bit kernels
* T launches on 5.10/5.15
* U launches on 5.15/6.1
@@ -307,19 +334,19 @@
}
// Ensure we can determine the Android build type.
- if (!android::bpf::isEng() && !android::bpf::isUser() && !android::bpf::isUserdebug()) {
+ if (!isEng() && !isUser() && !isUserdebug()) {
ALOGE("Failed to determine the build type: got %s, want 'eng', 'user', or 'userdebug'",
- android::bpf::getBuildType().c_str());
+ getBuildType().c_str());
return 1;
}
- if (false && isAtLeastV) {
+ if (isAtLeastV) {
// Linux 5.16-rc1 changed the default to 2 (disabled but changeable),
// but we need 0 (enabled)
// (this writeFile is known to fail on at least 4.19, but always defaults to 0 on
// pre-5.13, on 5.13+ it depends on CONFIG_BPF_UNPRIV_DEFAULT_OFF)
if (writeProcSysFile("/proc/sys/kernel/unprivileged_bpf_disabled", "0\n") &&
- android::bpf::isAtLeastKernelVersion(5, 13, 0)) return 1;
+ isAtLeastKernelVersion(5, 13, 0)) return 1;
}
if (isAtLeastU) {
@@ -373,14 +400,14 @@
int key = 1;
int value = 123;
- android::base::unique_fd map(
- android::bpf::createMap(BPF_MAP_TYPE_ARRAY, sizeof(key), sizeof(value), 2, 0));
- if (android::bpf::writeToMapEntry(map, &key, &value, BPF_ANY)) {
+ base::unique_fd map(
+ createMap(BPF_MAP_TYPE_ARRAY, sizeof(key), sizeof(value), 2, 0));
+ if (writeToMapEntry(map, &key, &value, BPF_ANY)) {
ALOGE("Critical kernel bug - failure to write into index 1 of 2 element bpf map array.");
return 1;
}
- if (false && isAtLeastV) {
+ if (isAtLeastV) {
ALOGI("done, transferring control to platform bpfloader.");
const char * args[] = { platformBpfLoader, NULL, };
@@ -392,3 +419,21 @@
ALOGI("mainline done!");
return 0;
}
+
+} // namespace bpf
+} // namespace android
+
+int main(int argc, char** argv, char * const envp[]) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+
+ if (argc == 2 && !strcmp(argv[1], "done")) {
+ // we're being re-exec'ed from platform bpfloader to 'finalize' things
+ if (!android::base::SetProperty("bpf.progs_loaded", "1")) {
+ ALOGE("Failed to set bpf.progs_loaded property to 1.");
+ return 125;
+ }
+ return 0;
+ }
+
+ return android::bpf::doLoad(argv, envp);
+}
diff --git a/netbpfload/loader.cpp b/netbpfload/loader.cpp
index 9dd0d2a..2b5f5c7 100644
--- a/netbpfload/loader.cpp
+++ b/netbpfload/loader.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "NetBpfLoader"
+#define LOG_TAG "NetBpfLoad"
#include <errno.h>
#include <fcntl.h>
@@ -512,7 +512,7 @@
ret = readSectionByIdx(elfFile, i, cs_temp.data);
if (ret) return ret;
- ALOGD("Loaded code section %d (%s)", i, name.c_str());
+ ALOGV("Loaded code section %d (%s)", i, name.c_str());
vector<string> csSymNames;
ret = getSectionSymNames(elfFile, oldName, csSymNames, STT_FUNC);
@@ -532,13 +532,13 @@
if (name == (".rel" + oldName)) {
ret = readSectionByIdx(elfFile, i + 1, cs_temp.rel_data);
if (ret) return ret;
- ALOGD("Loaded relo section %d (%s)", i, name.c_str());
+ ALOGV("Loaded relo section %d (%s)", i, name.c_str());
}
}
if (cs_temp.data.size() > 0) {
cs.push_back(std::move(cs_temp));
- ALOGD("Adding section %d to cs list", i);
+ ALOGV("Adding section %d to cs list", i);
}
}
return 0;
@@ -769,7 +769,7 @@
.max_entries = max_entries,
.map_flags = md[i].map_flags,
};
- if (isAtLeastKernelVersion(4, 14, 0))
+ if (isAtLeastKernelVersion(4, 15, 0))
strlcpy(req.map_name, mapNames[i].c_str(), sizeof(req.map_name));
fd.reset(bpf(BPF_MAP_CREATE, req));
saved_errno = errno;
@@ -869,7 +869,7 @@
// Occasionally might be useful for relocation debugging, but pretty spammy
if (0) {
- ALOGD("applying relo to instruction at byte offset: %llu, "
+ ALOGV("applying relo to instruction at byte offset: %llu, "
"insn offset %d, insn %llx",
(unsigned long long)offset, insnIndex, *(unsigned long long*)insn);
}
@@ -1012,7 +1012,7 @@
.log_size = static_cast<__u32>(log_buf.size()),
.expected_attach_type = cs[i].expected_attach_type,
};
- if (isAtLeastKernelVersion(4, 14, 0))
+ if (isAtLeastKernelVersion(4, 15, 0))
strlcpy(req.prog_name, cs[i].name.c_str(), sizeof(req.prog_name));
fd.reset(bpf(BPF_PROG_LOAD, req));
@@ -1177,7 +1177,7 @@
}
for (int i = 0; i < (int)mapFds.size(); i++)
- ALOGD("map_fd found at %d is %d in %s", i, mapFds[i].get(), elfPath);
+ ALOGV("map_fd found at %d is %d in %s", i, mapFds[i].get(), elfPath);
applyMapRelo(elfFile, mapFds, cs);
diff --git a/netbpfload/netbpfload.35rc b/netbpfload/netbpfload.35rc
new file mode 100644
index 0000000..0fbcb5a
--- /dev/null
+++ b/netbpfload/netbpfload.35rc
@@ -0,0 +1,9 @@
+service bpfloader /apex/com.android.tethering/bin/netbpfload
+ capabilities CHOWN SYS_ADMIN NET_ADMIN
+ group root graphics network_stack net_admin net_bw_acct net_bw_stats net_raw system
+ user root
+ file /dev/kmsg w
+ rlimit memlock 1073741824 1073741824
+ oneshot
+ reboot_on_failure reboot,bpfloader-failed
+ override
diff --git a/netd/BpfHandler.cpp b/netd/BpfHandler.cpp
index 0d75c05..b535ebf 100644
--- a/netd/BpfHandler.cpp
+++ b/netd/BpfHandler.cpp
@@ -183,7 +183,8 @@
// Make sure BPF programs are loaded before doing anything
ALOGI("Waiting for BPF programs");
- if (true || !modules::sdklevel::IsAtLeastV()) {
+ // TODO: use !modules::sdklevel::IsAtLeastV() once api finalized
+ if (android_get_device_api_level() < __ANDROID_API_V__) {
waitForNetProgsLoaded();
ALOGI("Networking BPF programs are loaded");
diff --git a/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java b/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
index 42a922d..385adc6 100644
--- a/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
+++ b/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
@@ -247,12 +247,15 @@
* @param isLegacy Whether this call is using legacy backend.
* @param transactionId The transaction id of service resolution.
* @param durationMs The duration before stop resolving the service.
+ * @param sentQueryCount The count of sent queries during resolving.
*/
- public void reportServiceResolutionStop(boolean isLegacy, int transactionId, long durationMs) {
+ public void reportServiceResolutionStop(boolean isLegacy, int transactionId, long durationMs,
+ int sentQueryCount) {
final Builder builder = makeReportedBuilder(isLegacy, transactionId);
builder.setType(NsdEventType.NET_RESOLVE);
builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_RESOLUTION_STOP);
builder.setEventDurationMillisec(durationMs);
+ builder.setSentQueryCount(sentQueryCount);
mDependencies.statsWrite(builder.build());
}
diff --git a/service-t/src/com/android/server/IpSecService.java b/service-t/src/com/android/server/IpSecService.java
index ea91e64..54b9ced 100644
--- a/service-t/src/com/android/server/IpSecService.java
+++ b/service-t/src/com/android/server/IpSecService.java
@@ -1877,6 +1877,10 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "IpsecService#getTransformState");
+ if (transformId == INVALID_RESOURCE_ID) {
+ throw new IllegalStateException("This transform is already closed");
+ }
+
UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
TransformRecord transformInfo =
userRecord.mTransformRecords.getResourceOrThrow(transformId);
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index 8552eec..0a8adf0 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -201,6 +201,7 @@
private static final int NO_SENT_QUERY_COUNT = 0;
private static final int DISCOVERY_QUERY_SENT_CALLBACK = 1000;
private static final int MAX_SUBTYPE_COUNT = 100;
+ private static final int DNSSEC_PROTOCOL = 3;
private static final SharedLog LOGGER = new SharedLog("serviceDiscovery");
private final Context mContext;
@@ -1009,6 +1010,17 @@
break;
}
+ if (!checkPublicKey(serviceInfo.getPublicKey())) {
+ Log.e(TAG,
+ "Invalid public key: "
+ + Arrays.toString(serviceInfo.getPublicKey()));
+ clientInfo.onRegisterServiceFailedImmediately(
+ clientRequestId,
+ NsdManager.FAILURE_BAD_PARAMETERS,
+ false /* isLegacy */);
+ break;
+ }
+
Set<String> subtypes = new ArraySet<>(serviceInfo.getSubtypes());
if (typeSubtype != null && typeSubtype.second != null) {
for (String subType : typeSubtype.second) {
@@ -1825,15 +1837,42 @@
* <p>For now NsdService only allows single-label hostnames conforming to RFC 1035. In other
* words, the hostname should be at most 63 characters long and it only contains letters, digits
* and hyphens.
+ *
+ * <p>Additionally, this allows hostname starting with a digit to support Matter devices. Per
+ * Matter spec 4.3.1.1:
+ *
+ * <p>The target host name SHALL be constructed using one of the available link-layer addresses,
+ * such as a 48-bit device MAC address (for Ethernet and Wi‑Fi) or a 64-bit MAC Extended Address
+ * (for Thread) expressed as a fixed-length twelve-character (or sixteen-character) hexadecimal
+ * string, encoded as ASCII (UTF-8) text using capital letters, e.g., B75AFB458ECD.<domain>.
*/
public static boolean checkHostname(@Nullable String hostname) {
if (hostname == null) {
return true;
}
- String HOSTNAME_REGEX = "^[a-zA-Z]([a-zA-Z0-9-_]{0,61}[a-zA-Z0-9])?$";
+ String HOSTNAME_REGEX = "^[a-zA-Z0-9]([a-zA-Z0-9-_]{0,61}[a-zA-Z0-9])?$";
return Pattern.compile(HOSTNAME_REGEX).matcher(hostname).matches();
}
+ /**
+ * Checks if the public key is valid.
+ *
+ * <p>For simplicity, it only checks if the protocol is DNSSEC and the RDATA is not fewer than 4
+ * bytes. See RFC 3445 Section 3.
+ *
+ * <p>Message format: flags (2 bytes), protocol (1 byte), algorithm (1 byte), public key.
+ */
+ private static boolean checkPublicKey(@Nullable byte[] publicKey) {
+ if (publicKey == null) {
+ return true;
+ }
+ if (publicKey.length < 4) {
+ return false;
+ }
+ int protocol = publicKey[2];
+ return protocol == DNSSEC_PROTOCOL;
+ }
+
/** Returns {@code true} if {@code subtype} is a valid DNS-SD subtype label. */
private static boolean checkSubtypeLabel(String subtype) {
return Pattern.compile("^" + SUBTYPE_LABEL_REGEX + "$").matcher(subtype).matches();
@@ -2851,7 +2890,8 @@
request.getSentQueryCount());
} else if (listener instanceof ResolutionListener) {
mMetrics.reportServiceResolutionStop(false /* isLegacy */, transactionId,
- request.calculateRequestDurationMs(mClock.elapsedRealtime()));
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()),
+ request.getSentQueryCount());
} else if (listener instanceof ServiceInfoListener) {
mMetrics.reportServiceInfoCallbackUnregistered(transactionId,
request.calculateRequestDurationMs(mClock.elapsedRealtime()),
@@ -2892,7 +2932,8 @@
case NsdManager.RESOLVE_SERVICE:
stopResolveService(transactionId);
mMetrics.reportServiceResolutionStop(true /* isLegacy */, transactionId,
- request.calculateRequestDurationMs(mClock.elapsedRealtime()));
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()),
+ NO_SENT_QUERY_COUNT);
break;
case NsdManager.REGISTER_SERVICE:
unregisterService(transactionId);
@@ -3108,7 +3149,8 @@
mMetrics.reportServiceResolutionStop(
isLegacyClientRequest(request),
request.mTransactionId,
- request.calculateRequestDurationMs(mClock.elapsedRealtime()));
+ request.calculateRequestDurationMs(mClock.elapsedRealtime()),
+ request.getSentQueryCount());
try {
mCb.onStopResolutionSucceeded(listenerKey);
} catch (RemoteException e) {
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java b/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
index 98c2d86..42efcac 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
@@ -448,10 +448,11 @@
/**
* Get the ID of a conflicting registration due to host, or -1 if none.
*
- * <p>It's valid that multiple registrations from the same user are using the same hostname.
- *
* <p>If there's already another registration with the same hostname requested by another
- * user, this is considered a conflict.
+ * user, this is a conflict.
+ *
+ * <p>If there're two registrations both containing address records using the same hostname,
+ * this is a conflict.
*/
int getConflictingRegistrationDueToHost(@NonNull NsdServiceInfo info, int clientUid) {
if (TextUtils.isEmpty(info.getHostname())) {
@@ -460,10 +461,17 @@
for (int i = 0; i < mPendingRegistrations.size(); i++) {
final Registration otherRegistration = mPendingRegistrations.valueAt(i);
final NsdServiceInfo otherInfo = otherRegistration.getServiceInfo();
+ final int otherServiceId = mPendingRegistrations.keyAt(i);
if (clientUid != otherRegistration.mClientUid
&& MdnsUtils.equalsIgnoreDnsCase(
info.getHostname(), otherInfo.getHostname())) {
- return mPendingRegistrations.keyAt(i);
+ return otherServiceId;
+ }
+ if (!info.getHostAddresses().isEmpty()
+ && !otherInfo.getHostAddresses().isEmpty()
+ && MdnsUtils.equalsIgnoreDnsCase(
+ info.getHostname(), otherInfo.getHostname())) {
+ return otherServiceId;
}
}
return -1;
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsKeyRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsKeyRecord.java
new file mode 100644
index 0000000..ba8a56e
--- /dev/null
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsKeyRecord.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2024 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.server.connectivity.mdns;
+
+import static com.android.net.module.util.HexDump.toHexString;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+/** An mDNS "KEY" record, which contains a public key for a name. See RFC 2535. */
+@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+public class MdnsKeyRecord extends MdnsRecord {
+ @Nullable private byte[] rData;
+
+ public MdnsKeyRecord(@NonNull String[] name, @NonNull MdnsPacketReader reader)
+ throws IOException {
+ this(name, reader, false);
+ }
+
+ public MdnsKeyRecord(@NonNull String[] name, @NonNull MdnsPacketReader reader,
+ boolean isQuestion) throws IOException {
+ super(name, TYPE_KEY, reader, isQuestion);
+ }
+
+ public MdnsKeyRecord(@NonNull String[] name, boolean isUnicast) {
+ super(name, TYPE_KEY,
+ MdnsConstants.QCLASS_INTERNET | (isUnicast ? MdnsConstants.QCLASS_UNICAST : 0),
+ 0L /* receiptTimeMillis */, false /* cacheFlush */, 0L /* ttlMillis */);
+ }
+
+ public MdnsKeyRecord(@NonNull String[] name, long receiptTimeMillis, boolean cacheFlush,
+ long ttlMillis, @Nullable byte[] rData) {
+ super(name, TYPE_KEY, MdnsConstants.QCLASS_INTERNET, receiptTimeMillis, cacheFlush,
+ ttlMillis);
+ if (rData != null) {
+ this.rData = Arrays.copyOf(rData, rData.length);
+ }
+ }
+ /** Returns the KEY RDATA in bytes **/
+ public byte[] getRData() {
+ if (rData == null) {
+ return null;
+ }
+ return Arrays.copyOf(rData, rData.length);
+ }
+
+ @Override
+ protected void readData(MdnsPacketReader reader) throws IOException {
+ rData = new byte[reader.getRemaining()];
+ reader.readBytes(rData);
+ }
+
+ @Override
+ protected void writeData(MdnsPacketWriter writer) throws IOException {
+ if (rData != null) {
+ writer.writeBytes(rData);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "KEY: " + toHexString(rData);
+ }
+
+ @Override
+ public int hashCode() {
+ return (super.hashCode() * 31) + Arrays.hashCode(rData);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof MdnsKeyRecord)) {
+ return false;
+ }
+
+ return super.equals(other) && Arrays.equals(rData, ((MdnsKeyRecord) other).rData);
+ }
+}
\ No newline at end of file
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsPacket.java b/service-t/src/com/android/server/connectivity/mdns/MdnsPacket.java
index 83ecabc..aef8211 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsPacket.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsPacket.java
@@ -196,6 +196,15 @@
}
}
+ case MdnsRecord.TYPE_KEY: {
+ try {
+ return new MdnsKeyRecord(name, reader, isQuestion);
+ } catch (IOException e) {
+ throw new ParseException(MdnsResponseErrorCode.ERROR_READING_KEY_RDATA,
+ "Failed to read KEY record from mDNS response.", e);
+ }
+ }
+
case MdnsRecord.TYPE_NSEC: {
try {
return new MdnsNsecRecord(name, reader, isQuestion);
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsRecord.java
index 1f9f42b..b865319 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsRecord.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsRecord.java
@@ -41,6 +41,7 @@
public static final int TYPE_PTR = 0x000C;
public static final int TYPE_SRV = 0x0021;
public static final int TYPE_TXT = 0x0010;
+ public static final int TYPE_KEY = 0x0019;
public static final int TYPE_NSEC = 0x002f;
public static final int TYPE_ANY = 0x00ff;
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java b/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java
index 073e465..39e8bcc 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java
@@ -97,6 +97,8 @@
@NonNull
private final Looper mLooper;
@NonNull
+ private final Dependencies mDeps;
+ @NonNull
private final String[] mDeviceHostname;
@NonNull
private final MdnsFeatureFlags mMdnsFeatureFlags;
@@ -111,6 +113,7 @@
@NonNull String[] deviceHostname, @NonNull MdnsFeatureFlags mdnsFeatureFlags) {
mDeviceHostname = deviceHostname;
mLooper = looper;
+ mDeps = deps;
mMdnsFeatureFlags = mdnsFeatureFlags;
}
@@ -127,6 +130,10 @@
public Enumeration<InetAddress> getInterfaceInetAddresses(@NonNull NetworkInterface iface) {
return iface.getInetAddresses();
}
+
+ public long elapsedRealTime() {
+ return SystemClock.elapsedRealtime();
+ }
}
private static class RecordInfo<T extends MdnsRecord> {
@@ -140,17 +147,25 @@
public final boolean isSharedName;
/**
- * Last time (as per SystemClock.elapsedRealtime) when advertised via multicast, 0 if never
+ * Last time (as per SystemClock.elapsedRealtime) when advertised via multicast on IPv4, 0
+ * if never
*/
- public long lastAdvertisedTimeMs;
+ public long lastAdvertisedOnIpv4TimeMs;
/**
- * Last time (as per SystemClock.elapsedRealtime) when sent via unicast or multicast,
- * 0 if never
+ * Last time (as per SystemClock.elapsedRealtime) when advertised via multicast on IPv6, 0
+ * if never
*/
- // FIXME: the `lastSentTimeMs` and `lastAdvertisedTimeMs` should be maintained separately
- // for IPv4 and IPv6, because neither IPv4 nor and IPv6 clients can receive replies in
- // different address space.
+ public long lastAdvertisedOnIpv6TimeMs;
+
+ /**
+ * Last time (as per SystemClock.elapsedRealtime) when sent via unicast or multicast, 0 if
+ * never.
+ *
+ * <p>Different from lastAdvertisedOnIpv(4|6)TimeMs, lastSentTimeMs is mainly used for
+ * tracking is a record is ever sent out, no matter unicast/multicast or IPv4/IPv6. It's
+ * unnecessary to maintain two versions (IPv4/IPv6) for it.
+ */
public long lastSentTimeMs;
RecordInfo(NsdServiceInfo serviceInfo, T record, boolean sharedName) {
@@ -169,6 +184,10 @@
public final RecordInfo<MdnsServiceRecord> srvRecord;
@Nullable
public final RecordInfo<MdnsTextRecord> txtRecord;
+ @Nullable
+ public final RecordInfo<MdnsKeyRecord> serviceKeyRecord;
+ @Nullable
+ public final RecordInfo<MdnsKeyRecord> hostKeyRecord;
@NonNull
public final List<RecordInfo<MdnsInetAddressRecord>> addressRecords;
@NonNull
@@ -230,7 +249,6 @@
nameRecordsTtlMillis = DEFAULT_NAME_RECORDS_TTL_MILLIS;
}
- final boolean hasService = !TextUtils.isEmpty(serviceInfo.getServiceType());
final boolean hasCustomHost = !TextUtils.isEmpty(serviceInfo.getHostname());
final String[] hostname =
hasCustomHost
@@ -238,9 +256,11 @@
: deviceHostname;
final ArrayList<RecordInfo<?>> allRecords = new ArrayList<>(5);
- if (hasService) {
- final String[] serviceType = splitServiceType(serviceInfo);
- final String[] serviceName = splitFullyQualifiedName(serviceInfo, serviceType);
+ final boolean hasService = !TextUtils.isEmpty(serviceInfo.getServiceType());
+ final String[] serviceType = hasService ? splitServiceType(serviceInfo) : null;
+ final String[] serviceName =
+ hasService ? splitFullyQualifiedName(serviceInfo, serviceType) : null;
+ if (hasService && hasSrvRecord(serviceInfo)) {
// Service PTR records
ptrRecords = new ArrayList<>(serviceInfo.getSubtypes().size() + 1);
ptrRecords.add(new RecordInfo<>(
@@ -321,6 +341,36 @@
addressRecords = Collections.emptyList();
}
+ final boolean hasKey = hasKeyRecord(serviceInfo);
+ if (hasKey && hasService) {
+ this.serviceKeyRecord = new RecordInfo<>(
+ serviceInfo,
+ new MdnsKeyRecord(
+ serviceName,
+ 0L /*receiptTimeMillis */,
+ true /* cacheFlush */,
+ nameRecordsTtlMillis,
+ serviceInfo.getPublicKey()),
+ false /* sharedName */);
+ allRecords.add(this.serviceKeyRecord);
+ } else {
+ this.serviceKeyRecord = null;
+ }
+ if (hasKey && hasCustomHost) {
+ this.hostKeyRecord = new RecordInfo<>(
+ serviceInfo,
+ new MdnsKeyRecord(
+ hostname,
+ 0L /*receiptTimeMillis */,
+ true /* cacheFlush */,
+ nameRecordsTtlMillis,
+ serviceInfo.getPublicKey()),
+ false /* sharedName */);
+ allRecords.add(this.hostKeyRecord);
+ } else {
+ this.hostKeyRecord = null;
+ }
+
this.allRecords = Collections.unmodifiableList(allRecords);
this.repliedServiceCount = repliedServiceCount;
this.sentPacketCount = sentPacketCount;
@@ -471,6 +521,22 @@
? inetAddressRecord.getInet6Address()
: inetAddressRecord.getInet4Address()));
}
+
+ List<MdnsKeyRecord> keyRecords = new ArrayList<>();
+ if (registration.serviceKeyRecord != null) {
+ keyRecords.add(registration.serviceKeyRecord.record);
+ }
+ if (registration.hostKeyRecord != null) {
+ keyRecords.add(registration.hostKeyRecord.record);
+ }
+ for (MdnsKeyRecord keyRecord : keyRecords) {
+ probingRecords.add(new MdnsKeyRecord(
+ keyRecord.getName(),
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ keyRecord.getTtl(),
+ keyRecord.getRData()));
+ }
return new MdnsProber.ProbingInfo(serviceId, probingRecords);
}
@@ -577,7 +643,8 @@
*/
@Nullable
public MdnsReplyInfo getReply(MdnsPacket packet, InetSocketAddress src) {
- final long now = SystemClock.elapsedRealtime();
+ final long now = mDeps.elapsedRealTime();
+ final boolean isQuestionOnIpv4 = src.getAddress() instanceof Inet4Address;
// TODO: b/322142420 - Set<RecordInfo<?>> may contain duplicate records wrapped in different
// RecordInfo<?>s when custom host is enabled.
@@ -595,7 +662,7 @@
null /* serviceSrvRecord */, null /* serviceTxtRecord */,
null /* hostname */,
replyUnicastEnabled, now, answerInfo, additionalAnswerInfo,
- Collections.emptyList())) {
+ Collections.emptyList(), isQuestionOnIpv4)) {
replyUnicast &= question.isUnicastReplyRequested();
}
@@ -607,7 +674,7 @@
registration.srvRecord, registration.txtRecord,
registration.serviceInfo.getHostname(),
replyUnicastEnabled, now,
- answerInfo, additionalAnswerInfo, packet.answers)) {
+ answerInfo, additionalAnswerInfo, packet.answers, isQuestionOnIpv4)) {
replyUnicast &= question.isUnicastReplyRequested();
registration.repliedServiceCount++;
registration.sentPacketCount++;
@@ -685,7 +752,7 @@
// multicast responses. Unicast replies are faster as they do not need to wait for the
// beacon interval on Wi-Fi.
dest = src;
- } else if (src.getAddress() instanceof Inet4Address) {
+ } else if (isQuestionOnIpv4) {
dest = IPV4_SOCKET_ADDR;
} else {
dest = IPV6_SOCKET_ADDR;
@@ -697,7 +764,11 @@
// TODO: consider actual packet send delay after response aggregation
info.lastSentTimeMs = now + delayMs;
if (!replyUnicast) {
- info.lastAdvertisedTimeMs = info.lastSentTimeMs;
+ if (isQuestionOnIpv4) {
+ info.lastAdvertisedOnIpv4TimeMs = info.lastSentTimeMs;
+ } else {
+ info.lastAdvertisedOnIpv6TimeMs = info.lastSentTimeMs;
+ }
}
// Different RecordInfos may the contain the same record
if (!answerRecords.contains(info.record)) {
@@ -729,7 +800,8 @@
@Nullable String hostname,
boolean replyUnicastEnabled, long now, @NonNull Set<RecordInfo<?>> answerInfo,
@NonNull Set<RecordInfo<?>> additionalAnswerInfo,
- @NonNull List<MdnsRecord> knownAnswerRecords) {
+ @NonNull List<MdnsRecord> knownAnswerRecords,
+ boolean isQuestionOnIpv4) {
boolean hasDnsSdPtrRecordAnswer = false;
boolean hasDnsSdSrvRecordAnswer = false;
boolean hasFullyOwnedNameMatch = false;
@@ -778,10 +850,20 @@
// TODO: responses to probe queries should bypass this check and only ensure the
// reply is sent 250ms after the last sent time (RFC 6762 p.15)
- if (!(replyUnicastEnabled && question.isUnicastReplyRequested())
- && info.lastAdvertisedTimeMs > 0L
- && now - info.lastAdvertisedTimeMs < MIN_MULTICAST_REPLY_INTERVAL_MS) {
- continue;
+ if (!(replyUnicastEnabled && question.isUnicastReplyRequested())) {
+ if (isQuestionOnIpv4) { // IPv4
+ if (info.lastAdvertisedOnIpv4TimeMs > 0L
+ && now - info.lastAdvertisedOnIpv4TimeMs
+ < MIN_MULTICAST_REPLY_INTERVAL_MS) {
+ continue;
+ }
+ } else { // IPv6
+ if (info.lastAdvertisedOnIpv6TimeMs > 0L
+ && now - info.lastAdvertisedOnIpv6TimeMs
+ < MIN_MULTICAST_REPLY_INTERVAL_MS) {
+ continue;
+ }
+ }
}
answerInfo.add(info);
@@ -1070,18 +1152,15 @@
Collections.emptyList() /* additionalRecords */);
}
- /** Check if the record is in any service registration */
- private boolean hasInetAddressRecord(@NonNull MdnsInetAddressRecord record) {
- for (int i = 0; i < mServices.size(); i++) {
- final ServiceRegistration registration = mServices.valueAt(i);
- if (registration.exiting) continue;
-
- for (RecordInfo<MdnsInetAddressRecord> localRecord : registration.addressRecords) {
- if (Objects.equals(localRecord.record, record)) {
- return true;
- }
+ /** Check if the record is in a registration */
+ private static boolean hasInetAddressRecord(
+ @NonNull ServiceRegistration registration, @NonNull MdnsInetAddressRecord record) {
+ for (RecordInfo<MdnsInetAddressRecord> localRecord : registration.addressRecords) {
+ if (Objects.equals(localRecord.record, record)) {
+ return true;
}
}
+
return false;
}
@@ -1124,36 +1203,33 @@
return conflicting;
}
-
private static boolean conflictForService(
@NonNull MdnsRecord record, @NonNull ServiceRegistration registration) {
- if (registration.srvRecord == null) {
+ String[] fullServiceName;
+ if (registration.srvRecord != null) {
+ fullServiceName = registration.srvRecord.record.getName();
+ } else if (registration.serviceKeyRecord != null) {
+ fullServiceName = registration.serviceKeyRecord.record.getName();
+ } else {
return false;
}
- final RecordInfo<MdnsServiceRecord> srvRecord = registration.srvRecord;
- if (!MdnsUtils.equalsDnsLabelIgnoreDnsCase(record.getName(), srvRecord.record.getName())) {
+ if (!MdnsUtils.equalsDnsLabelIgnoreDnsCase(record.getName(), fullServiceName)) {
return false;
}
// As per RFC6762 9., it's fine if the "conflict" is an identical record with same
// data.
- if (record instanceof MdnsServiceRecord) {
- final MdnsServiceRecord local = srvRecord.record;
- final MdnsServiceRecord other = (MdnsServiceRecord) record;
- // Note "equals" does not consider TTL or receipt time, as intended here
- if (Objects.equals(local, other)) {
- return false;
- }
+ if (record instanceof MdnsServiceRecord && equals(record, registration.srvRecord)) {
+ return false;
+ }
+ if (record instanceof MdnsTextRecord && equals(record, registration.txtRecord)) {
+ return false;
+ }
+ if (record instanceof MdnsKeyRecord && equals(record, registration.serviceKeyRecord)) {
+ return false;
}
- if (record instanceof MdnsTextRecord) {
- final MdnsTextRecord local = registration.txtRecord.record;
- final MdnsTextRecord other = (MdnsTextRecord) record;
- if (Objects.equals(local, other)) {
- return false;
- }
- }
return true;
}
@@ -1165,6 +1241,11 @@
return false;
}
+ // It cannot be a hostname conflict because not record is registered with the hostname.
+ if (registration.addressRecords.isEmpty() && registration.hostKeyRecord == null) {
+ return false;
+ }
+
// The record's name cannot be registered by NsdManager so it's not a conflict.
if (record.getName().length != 2 || !record.getName()[1].equals(LOCAL_TLD)) {
return false;
@@ -1176,13 +1257,26 @@
return false;
}
- // If this registration has any address record and there's no identical record in the
- // repository, it's a conflict. There will be no conflict if no registration has addresses
- // for that hostname.
- if (record instanceof MdnsInetAddressRecord) {
- if (!registration.addressRecords.isEmpty()) {
- return !hasInetAddressRecord((MdnsInetAddressRecord) record);
- }
+ // As per RFC6762 9., it's fine if the "conflict" is an identical record with same
+ // data.
+ if (record instanceof MdnsInetAddressRecord
+ && hasInetAddressRecord(registration, (MdnsInetAddressRecord) record)) {
+ return false;
+ }
+ if (record instanceof MdnsKeyRecord && equals(record, registration.hostKeyRecord)) {
+ return false;
+ }
+
+ // Per RFC 6762 8.1, when a record is being probed, any answer containing a record with that
+ // name, of any type, MUST be considered a conflicting response.
+ if (registration.isProbing) {
+ return true;
+ }
+ if (record instanceof MdnsInetAddressRecord && !registration.addressRecords.isEmpty()) {
+ return true;
+ }
+ if (record instanceof MdnsKeyRecord && registration.hostKeyRecord != null) {
+ return true;
}
return false;
@@ -1302,10 +1396,11 @@
final ServiceRegistration registration = mServices.get(serviceId);
if (registration == null) return;
- final long now = SystemClock.elapsedRealtime();
+ final long now = mDeps.elapsedRealTime();
for (RecordInfo<?> record : registration.allRecords) {
record.lastSentTimeMs = now;
- record.lastAdvertisedTimeMs = now;
+ record.lastAdvertisedOnIpv4TimeMs = now;
+ record.lastAdvertisedOnIpv6TimeMs = now;
}
registration.sentPacketCount += sentPacketCount;
}
@@ -1370,4 +1465,21 @@
return type;
}
+
+ /** Returns whether there will be an SRV record when registering the {@code info}. */
+ private static boolean hasSrvRecord(@NonNull NsdServiceInfo info) {
+ return info.getPort() > 0;
+ }
+
+ /** Returns whether there will be KEY record(s) when registering the {@code info}. */
+ private static boolean hasKeyRecord(@NonNull NsdServiceInfo info) {
+ return info.getPublicKey() != null;
+ }
+
+ private static boolean equals(@NonNull MdnsRecord record, @Nullable RecordInfo<?> recordInfo) {
+ if (recordInfo == null) {
+ return false;
+ }
+ return Objects.equals(record, recordInfo.record);
+ }
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsResponseErrorCode.java b/service-t/src/com/android/server/connectivity/mdns/MdnsResponseErrorCode.java
index 73a7e3a..f509da2 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsResponseErrorCode.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsResponseErrorCode.java
@@ -37,4 +37,5 @@
public static final int ERROR_END_OF_FILE = 12;
public static final int ERROR_READING_NSEC_RDATA = 13;
public static final int ERROR_READING_ANY_RDATA = 14;
+ public static final int ERROR_READING_KEY_RDATA = 15;
}
\ No newline at end of file
diff --git a/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java b/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java
index b8689d6..92f1953 100644
--- a/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java
+++ b/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java
@@ -108,7 +108,11 @@
PermissionUtils.enforceRestrictedNetworkPermission(mContext, TAG);
}
- return new IpConfiguration(mTracker.getIpConfiguration(iface));
+ // This causes thread-unsafe access on mIpConfigurations which might
+ // race with calls to EthernetManager#updateConfiguration().
+ // EthernetManager#getConfiguration() has been marked as
+ // @UnsupportedAppUsage since Android R.
+ return mTracker.getIpConfiguration(iface);
}
/**
diff --git a/service-t/src/com/android/server/ethernet/EthernetTracker.java b/service-t/src/com/android/server/ethernet/EthernetTracker.java
index 71f289e..a60592f 100644
--- a/service-t/src/com/android/server/ethernet/EthernetTracker.java
+++ b/service-t/src/com/android/server/ethernet/EthernetTracker.java
@@ -31,8 +31,6 @@
import android.net.ITetheredInterfaceCallback;
import android.net.InterfaceConfigurationParcel;
import android.net.IpConfiguration;
-import android.net.IpConfiguration.IpAssignment;
-import android.net.IpConfiguration.ProxySettings;
import android.net.LinkAddress;
import android.net.NetworkCapabilities;
import android.net.StaticIpConfiguration;
@@ -111,6 +109,7 @@
/** Mapping between {iface name | mac address} -> {NetworkCapabilities} */
private final ConcurrentHashMap<String, NetworkCapabilities> mNetworkCapabilities =
new ConcurrentHashMap<>();
+ /** Mapping between {iface name | mac address} -> {IpConfiguration} */
private final ConcurrentHashMap<String, IpConfiguration> mIpConfigurations =
new ConcurrentHashMap<>();
@@ -298,7 +297,7 @@
}
private IpConfiguration getIpConfigurationForCallback(String iface, int state) {
- return (state == EthernetManager.STATE_ABSENT) ? null : getOrCreateIpConfiguration(iface);
+ return (state == EthernetManager.STATE_ABSENT) ? null : getIpConfiguration(iface);
}
private void ensureRunningOnEthernetServiceThread() {
@@ -391,8 +390,83 @@
mHandler.post(() -> setInterfaceAdministrativeState(iface, enabled, cb));
}
- IpConfiguration getIpConfiguration(String iface) {
- return mIpConfigurations.get(iface);
+ private @Nullable String getHwAddress(String iface) {
+ if (getInterfaceRole(iface) == EthernetManager.ROLE_SERVER) {
+ return mTetheringInterfaceHwAddr;
+ }
+
+ return mFactory.getHwAddress(iface);
+ }
+
+ /**
+ * Get the IP configuration of the interface, or the default if the interface doesn't exist.
+ * @param iface the name of the interface to retrieve.
+ *
+ * @return The IP configuration
+ */
+ public IpConfiguration getIpConfiguration(String iface) {
+ return getIpConfiguration(iface, getHwAddress(iface));
+ }
+
+ private IpConfiguration getIpConfiguration(String iface, @Nullable String hwAddress) {
+ // Look up Ip configuration first by ifname, then by MAC address.
+ IpConfiguration ipConfig = mIpConfigurations.get(iface);
+ if (ipConfig != null) {
+ return ipConfig;
+ }
+
+ if (hwAddress == null) {
+ // should never happen.
+ Log.wtf(TAG, "No hardware address for interface " + iface);
+ } else {
+ ipConfig = mIpConfigurations.get(hwAddress);
+ }
+
+ if (ipConfig == null) {
+ ipConfig = new IpConfiguration.Builder().build();
+ }
+
+ return ipConfig;
+ }
+
+ private NetworkCapabilities getNetworkCapabilities(String iface) {
+ return getNetworkCapabilities(iface, getHwAddress(iface));
+ }
+
+ private NetworkCapabilities getNetworkCapabilities(String iface, @Nullable String hwAddress) {
+ // Look up network capabilities first by ifname, then by MAC address.
+ NetworkCapabilities networkCapabilities = mNetworkCapabilities.get(iface);
+ if (networkCapabilities != null) {
+ return networkCapabilities;
+ }
+
+ if (hwAddress == null) {
+ // should never happen.
+ Log.wtf(TAG, "No hardware address for interface " + iface);
+ } else {
+ networkCapabilities = mNetworkCapabilities.get(hwAddress);
+ }
+
+ if (networkCapabilities != null) {
+ return networkCapabilities;
+ }
+
+ final NetworkCapabilities.Builder builder = createNetworkCapabilities(
+ false /* clear default capabilities */, null, null)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+
+ if (isValidTestInterface(iface)) {
+ builder.addTransportType(NetworkCapabilities.TRANSPORT_TEST);
+ } else {
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ }
+
+ return builder.build();
}
@VisibleForTesting(visibility = PACKAGE)
@@ -433,8 +507,8 @@
* NET_CAPABILITY_NOT_RESTRICTED) capability. Otherwise, returns false.
*/
boolean isRestrictedInterface(String iface) {
- final NetworkCapabilities nc = mNetworkCapabilities.get(iface);
- return nc != null && !nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+ final NetworkCapabilities nc = getNetworkCapabilities(iface);
+ return !nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
}
void addListener(IEthernetServiceListener listener, boolean canUseRestrictedNetworks) {
@@ -623,17 +697,9 @@
return;
}
- NetworkCapabilities nc = mNetworkCapabilities.get(iface);
- if (nc == null) {
- // Try to resolve using mac address
- nc = mNetworkCapabilities.get(hwAddress);
- if (nc == null) {
- final boolean isTestIface = iface.matches(TEST_IFACE_REGEXP);
- nc = createDefaultNetworkCapabilities(isTestIface);
- }
- }
+ final NetworkCapabilities nc = getNetworkCapabilities(iface, hwAddress);
+ final IpConfiguration ipConfiguration = getIpConfiguration(iface, hwAddress);
- IpConfiguration ipConfiguration = getOrCreateIpConfiguration(iface);
Log.d(TAG, "Tracking interface in client mode: " + iface);
mFactory.addInterface(iface, hwAddress, ipConfiguration, nc);
@@ -773,25 +839,6 @@
return new EthernetTrackerConfig(configString.split(";", /* limit of tokens */ 4));
}
- private static NetworkCapabilities createDefaultNetworkCapabilities(boolean isTestIface) {
- NetworkCapabilities.Builder builder = createNetworkCapabilities(
- false /* clear default capabilities */, null, null)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
-
- if (isTestIface) {
- builder.addTransportType(NetworkCapabilities.TRANSPORT_TEST);
- } else {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
- }
-
- return builder.build();
- }
-
/**
* Parses a static list of network capabilities
*
@@ -926,15 +973,6 @@
return new IpConfiguration.Builder().setStaticIpConfiguration(staticIpConfig).build();
}
- private IpConfiguration getOrCreateIpConfiguration(String iface) {
- IpConfiguration ret = mIpConfigurations.get(iface);
- if (ret != null) return ret;
- ret = new IpConfiguration();
- ret.setIpAssignment(IpAssignment.DHCP);
- ret.setProxySettings(ProxySettings.NONE);
- return ret;
- }
-
private boolean isValidEthernetInterface(String iface) {
return iface.matches(mIfaceMatch) || isValidTestInterface(iface);
}
@@ -1021,7 +1059,7 @@
pw.println("IP Configurations:");
pw.increaseIndent();
for (String iface : mIpConfigurations.keySet()) {
- pw.println(iface + ": " + mIpConfigurations.get(iface));
+ pw.println(iface + ": " + getIpConfiguration(iface));
}
pw.decreaseIndent();
pw.println();
@@ -1029,7 +1067,7 @@
pw.println("Network Capabilities:");
pw.increaseIndent();
for (String iface : mNetworkCapabilities.keySet()) {
- pw.println(iface + ": " + mNetworkCapabilities.get(iface));
+ pw.println(iface + ": " + getNetworkCapabilities(iface));
}
pw.decreaseIndent();
pw.println();
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index 5e98ee1..8305c1e 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -16,6 +16,7 @@
package com.android.server.net;
+import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.NETWORK_STATS_PROVIDER;
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
@@ -50,12 +51,17 @@
import static android.net.NetworkTemplate.MATCH_WIFI;
import static android.net.TrafficStats.KB_IN_BYTES;
import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.net.TrafficStats.TYPE_RX_BYTES;
+import static android.net.TrafficStats.TYPE_RX_PACKETS;
+import static android.net.TrafficStats.TYPE_TX_BYTES;
+import static android.net.TrafficStats.TYPE_TX_PACKETS;
import static android.net.TrafficStats.UID_TETHERING;
import static android.net.TrafficStats.UNSUPPORTED;
import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID;
import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID_TAG;
import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_XT;
import static android.os.Trace.TRACE_TAG_NETWORK;
+import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
import static android.system.OsConstants.ENOENT;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
@@ -64,6 +70,7 @@
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+import static com.android.net.module.util.DeviceConfigUtils.getDeviceConfigPropertyInt;
import static com.android.net.module.util.NetworkCapabilitiesUtils.getDisplayTransport;
import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT;
import static com.android.server.net.NetworkStatsEventLogger.POLL_REASON_PERIODIC;
@@ -299,6 +306,12 @@
static final String NETSTATS_FASTDATAINPUT_SUCCESSES_COUNTER_NAME = "fastdatainput.successes";
static final String NETSTATS_FASTDATAINPUT_FALLBACKS_COUNTER_NAME = "fastdatainput.fallbacks";
+ static final String TRAFFIC_STATS_CACHE_EXPIRY_DURATION_NAME =
+ "trafficstats_cache_expiry_duration_ms";
+ static final String TRAFFIC_STATS_CACHE_MAX_ENTRIES_NAME = "trafficstats_cache_max_entries";
+ static final int DEFAULT_TRAFFIC_STATS_CACHE_EXPIRY_DURATION_MS = 1000;
+ static final int DEFAULT_TRAFFIC_STATS_CACHE_MAX_ENTRIES = 400;
+
private final Context mContext;
private final NetworkStatsFactory mStatsFactory;
private final AlarmManager mAlarmManager;
@@ -454,6 +467,13 @@
private long mLastStatsSessionPoll;
+ private final TrafficStatsRateLimitCache mTrafficStatsTotalCache;
+ private final TrafficStatsRateLimitCache mTrafficStatsIfaceCache;
+ private final TrafficStatsRateLimitCache mTrafficStatsUidCache;
+ static final String TRAFFICSTATS_RATE_LIMIT_CACHE_ENABLED_FLAG =
+ "trafficstats_rate_limit_cache_enabled_flag";
+ private final boolean mSupportTrafficStatsRateLimitCache;
+
private final Object mOpenSessionCallsLock = new Object();
/**
@@ -643,6 +663,16 @@
mEventLogger = null;
}
+ final long cacheExpiryDurationMs = mDeps.getTrafficStatsRateLimitCacheExpiryDuration();
+ final int cacheMaxEntries = mDeps.getTrafficStatsRateLimitCacheMaxEntries();
+ mSupportTrafficStatsRateLimitCache = mDeps.supportTrafficStatsRateLimitCache(mContext);
+ mTrafficStatsTotalCache = new TrafficStatsRateLimitCache(mClock,
+ cacheExpiryDurationMs, cacheMaxEntries);
+ mTrafficStatsIfaceCache = new TrafficStatsRateLimitCache(mClock,
+ cacheExpiryDurationMs, cacheMaxEntries);
+ mTrafficStatsUidCache = new TrafficStatsRateLimitCache(mClock,
+ cacheExpiryDurationMs, cacheMaxEntries);
+
// TODO: Remove bpfNetMaps creation and always start SkDestroyListener
// Following code is for the experiment to verify the SkDestroyListener refactoring. Based
// on the experiment flag, BpfNetMaps starts C SkDestroyListener (existing code) or
@@ -696,7 +726,7 @@
* Get the count of import legacy target attempts.
*/
public int getImportLegacyTargetAttempts() {
- return DeviceConfigUtils.getDeviceConfigPropertyInt(
+ return getDeviceConfigPropertyInt(
DeviceConfig.NAMESPACE_TETHERING,
NETSTATS_IMPORT_LEGACY_TARGET_ATTEMPTS,
DEFAULT_NETSTATS_IMPORT_LEGACY_TARGET_ATTEMPTS);
@@ -706,7 +736,7 @@
* Get the count of using FastDataInput target attempts.
*/
public int getUseFastDataInputTargetAttempts() {
- return DeviceConfigUtils.getDeviceConfigPropertyInt(
+ return getDeviceConfigPropertyInt(
DeviceConfig.NAMESPACE_TETHERING,
NETSTATS_FASTDATAINPUT_TARGET_ATTEMPTS, 0);
}
@@ -888,6 +918,75 @@
return DeviceConfigUtils.isTetheringFeatureNotChickenedOut(
ctx, CONFIG_ENABLE_NETWORK_STATS_EVENT_LOGGER);
}
+
+ /**
+ * Get whether TrafficStats rate-limit cache is supported.
+ *
+ * This method should only be called once in the constructor,
+ * to ensure that the code does not need to deal with flag values changing at runtime.
+ */
+ public boolean supportTrafficStatsRateLimitCache(@NonNull Context ctx) {
+ return false;
+ }
+
+ /**
+ * Get TrafficStats rate-limit cache expiry.
+ *
+ * This method should only be called once in the constructor,
+ * to ensure that the code does not need to deal with flag values changing at runtime.
+ */
+ public int getTrafficStatsRateLimitCacheExpiryDuration() {
+ return getDeviceConfigPropertyInt(
+ NAMESPACE_TETHERING, TRAFFIC_STATS_CACHE_EXPIRY_DURATION_NAME,
+ DEFAULT_TRAFFIC_STATS_CACHE_EXPIRY_DURATION_MS);
+ }
+
+ /**
+ * Get TrafficStats rate-limit cache max entries.
+ *
+ * This method should only be called once in the constructor,
+ * to ensure that the code does not need to deal with flag values changing at runtime.
+ */
+ public int getTrafficStatsRateLimitCacheMaxEntries() {
+ return getDeviceConfigPropertyInt(
+ NAMESPACE_TETHERING, TRAFFIC_STATS_CACHE_MAX_ENTRIES_NAME,
+ DEFAULT_TRAFFIC_STATS_CACHE_MAX_ENTRIES);
+ }
+
+ /**
+ * Retrieves native network total statistics.
+ *
+ * @return A NetworkStats.Entry containing the native statistics, or
+ * null if an error occurs.
+ */
+ @Nullable
+ public NetworkStats.Entry nativeGetTotalStat() {
+ return NetworkStatsService.nativeGetTotalStat();
+ }
+
+ /**
+ * Retrieves native network interface statistics for the specified interface.
+ *
+ * @param iface The name of the network interface to query.
+ * @return A NetworkStats.Entry containing the native statistics for the interface, or
+ * null if an error occurs.
+ */
+ @Nullable
+ public NetworkStats.Entry nativeGetIfaceStat(String iface) {
+ return NetworkStatsService.nativeGetIfaceStat(iface);
+ }
+
+ /**
+ * Retrieves native network uid statistics for the specified uid.
+ *
+ * @param uid The uid of the application to query.
+ * @return A NetworkStats.Entry containing the native statistics for the uid, or
+ * null if an error occurs.
+ */
+ @Nullable
+ public NetworkStats.Entry nativeGetUidStat(int uid) {
+ return NetworkStatsService.nativeGetUidStat(uid);
+ }
}
/**
@@ -1983,53 +2082,106 @@
if (callingUid != android.os.Process.SYSTEM_UID && callingUid != uid) {
return UNSUPPORTED;
}
- return getEntryValueForType(nativeGetUidStat(uid), type);
+ if (!isEntryValueTypeValid(type)) return UNSUPPORTED;
+
+ if (!mSupportTrafficStatsRateLimitCache) {
+ return getEntryValueForType(mDeps.nativeGetUidStat(uid), type);
+ }
+
+ final NetworkStats.Entry entry = mTrafficStatsUidCache.getOrCompute(IFACE_ALL, uid,
+ () -> mDeps.nativeGetUidStat(uid));
+
+ return getEntryValueForType(entry, type);
+ }
+
+ @Nullable
+ private NetworkStats.Entry getIfaceStatsInternal(@NonNull String iface) {
+ final NetworkStats.Entry entry = mDeps.nativeGetIfaceStat(iface);
+ if (entry == null) {
+ return null;
+ }
+ // When tethering offload is in use, nativeIfaceStats does not contain usage from
+ // offload, add it back here. Note that the included statistics might be stale
+ // since polling newest stats from hardware might impact system health and not
+ // suitable for TrafficStats API use cases.
+ entry.add(getProviderIfaceStats(iface));
+ return entry;
}
@Override
public long getIfaceStats(@NonNull String iface, int type) {
Objects.requireNonNull(iface);
- final NetworkStats.Entry entry = nativeGetIfaceStat(iface);
- final long value = getEntryValueForType(entry, type);
- if (value == UNSUPPORTED) {
- return UNSUPPORTED;
- } else {
- // When tethering offload is in use, nativeIfaceStats does not contain usage from
- // offload, add it back here. Note that the included statistics might be stale
- // since polling newest stats from hardware might impact system health and not
- // suitable for TrafficStats API use cases.
- entry.add(getProviderIfaceStats(iface));
- return getEntryValueForType(entry, type);
+ if (!isEntryValueTypeValid(type)) return UNSUPPORTED;
+
+ if (!mSupportTrafficStatsRateLimitCache) {
+ return getEntryValueForType(getIfaceStatsInternal(iface), type);
}
+
+ final NetworkStats.Entry entry = mTrafficStatsIfaceCache.getOrCompute(iface, UID_ALL,
+ () -> getIfaceStatsInternal(iface));
+
+ return getEntryValueForType(entry, type);
}
private long getEntryValueForType(@Nullable NetworkStats.Entry entry, int type) {
if (entry == null) return UNSUPPORTED;
+ if (!isEntryValueTypeValid(type)) return UNSUPPORTED;
switch (type) {
- case TrafficStats.TYPE_RX_BYTES:
+ case TYPE_RX_BYTES:
return entry.rxBytes;
- case TrafficStats.TYPE_TX_BYTES:
- return entry.txBytes;
- case TrafficStats.TYPE_RX_PACKETS:
+ case TYPE_RX_PACKETS:
return entry.rxPackets;
- case TrafficStats.TYPE_TX_PACKETS:
+ case TYPE_TX_BYTES:
+ return entry.txBytes;
+ case TYPE_TX_PACKETS:
return entry.txPackets;
default:
- return UNSUPPORTED;
+ throw new IllegalStateException("Bug: Invalid type: "
+ + type + " should not reach here.");
}
}
+ private boolean isEntryValueTypeValid(int type) {
+ switch (type) {
+ case TYPE_RX_BYTES:
+ case TYPE_RX_PACKETS:
+ case TYPE_TX_BYTES:
+ case TYPE_TX_PACKETS:
+ return true;
+ default :
+ return false;
+ }
+ }
+
+ @Nullable
+ private NetworkStats.Entry getTotalStatsInternal() {
+ final NetworkStats.Entry entry = mDeps.nativeGetTotalStat();
+ if (entry == null) {
+ return null;
+ }
+ entry.add(getProviderIfaceStats(IFACE_ALL));
+ return entry;
+ }
+
@Override
public long getTotalStats(int type) {
- final NetworkStats.Entry entry = nativeGetTotalStat();
- final long value = getEntryValueForType(entry, type);
- if (value == UNSUPPORTED) {
- return UNSUPPORTED;
- } else {
- // Refer to comment in getIfaceStats
- entry.add(getProviderIfaceStats(IFACE_ALL));
- return getEntryValueForType(entry, type);
+ if (!isEntryValueTypeValid(type)) return UNSUPPORTED;
+ if (!mSupportTrafficStatsRateLimitCache) {
+ return getEntryValueForType(getTotalStatsInternal(), type);
}
+
+ final NetworkStats.Entry entry = mTrafficStatsTotalCache.getOrCompute(IFACE_ALL, UID_ALL,
+ () -> getTotalStatsInternal());
+
+ return getEntryValueForType(entry, type);
+ }
+
+ @Override
+ public void clearTrafficStatsRateLimitCaches() {
+ PermissionUtils.enforceNetworkStackPermissionOr(mContext, NETWORK_SETTINGS);
+ mTrafficStatsUidCache.clear();
+ mTrafficStatsIfaceCache.clear();
+ mTrafficStatsTotalCache.clear();
}
private NetworkStats.Entry getProviderIfaceStats(@Nullable String iface) {
@@ -2785,6 +2937,14 @@
} catch (IOException e) {
pw.println("(failed to dump FastDataInput counters)");
}
+ pw.print("trafficstats.cache.supported", mSupportTrafficStatsRateLimitCache);
+ pw.println();
+ pw.print(TRAFFIC_STATS_CACHE_EXPIRY_DURATION_NAME,
+ mDeps.getTrafficStatsRateLimitCacheExpiryDuration());
+ pw.println();
+ pw.print(TRAFFIC_STATS_CACHE_MAX_ENTRIES_NAME,
+ mDeps.getTrafficStatsRateLimitCacheMaxEntries());
+ pw.println();
pw.decreaseIndent();
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
index 4214bc9..c07d050 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -114,7 +114,8 @@
V("/sys/fs/bpf", S_IFDIR|S_ISVTX|0777, ROOT, ROOT, "fs_bpf", DIR);
- if (false && modules::sdklevel::IsAtLeastV()) {
+ // TODO: use modules::sdklevel::IsAtLeastV() once api finalized
+ if (android_get_device_api_level() >= __ANDROID_API_V__) {
V("/sys/fs/bpf/net_shared", S_IFDIR|01777, ROOT, ROOT, "fs_bpf_net_shared", DIR);
} else {
V("/sys/fs/bpf/net_shared", S_IFDIR|01777, SYSTEM, SYSTEM, "fs_bpf_net_shared", DIR);
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index 42c1628..04d8ea4 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -23,11 +23,9 @@
import static android.net.BpfNetMapsConstants.DATA_SAVER_ENABLED;
import static android.net.BpfNetMapsConstants.DATA_SAVER_ENABLED_KEY;
import static android.net.BpfNetMapsConstants.DATA_SAVER_ENABLED_MAP_PATH;
-import static android.net.BpfNetMapsConstants.HAPPY_BOX_MATCH;
import static android.net.BpfNetMapsConstants.IIF_MATCH;
import static android.net.BpfNetMapsConstants.INGRESS_DISCARD_MAP_PATH;
import static android.net.BpfNetMapsConstants.LOCKDOWN_VPN_MATCH;
-import static android.net.BpfNetMapsConstants.PENALTY_BOX_MATCH;
import static android.net.BpfNetMapsConstants.UID_OWNER_MAP_PATH;
import static android.net.BpfNetMapsConstants.UID_PERMISSION_MAP_PATH;
import static android.net.BpfNetMapsConstants.UID_RULES_CONFIGURATION_KEY;
@@ -446,62 +444,6 @@
}
/**
- * Add naughty app bandwidth rule for specific app
- *
- * @param uid uid of target app
- * @throws ServiceSpecificException in case of failure, with an error code indicating the
- * cause of the failure.
- */
- @RequiresApi(Build.VERSION_CODES.TIRAMISU)
- public void addNaughtyApp(final int uid) {
- throwIfPreT("addNaughtyApp is not available on pre-T devices");
-
- addRule(uid, PENALTY_BOX_MATCH, "addNaughtyApp");
- }
-
- /**
- * Remove naughty app bandwidth rule for specific app
- *
- * @param uid uid of target app
- * @throws ServiceSpecificException in case of failure, with an error code indicating the
- * cause of the failure.
- */
- @RequiresApi(Build.VERSION_CODES.TIRAMISU)
- public void removeNaughtyApp(final int uid) {
- throwIfPreT("removeNaughtyApp is not available on pre-T devices");
-
- removeRule(uid, PENALTY_BOX_MATCH, "removeNaughtyApp");
- }
-
- /**
- * Add nice app bandwidth rule for specific app
- *
- * @param uid uid of target app
- * @throws ServiceSpecificException in case of failure, with an error code indicating the
- * cause of the failure.
- */
- @RequiresApi(Build.VERSION_CODES.TIRAMISU)
- public void addNiceApp(final int uid) {
- throwIfPreT("addNiceApp is not available on pre-T devices");
-
- addRule(uid, HAPPY_BOX_MATCH, "addNiceApp");
- }
-
- /**
- * Remove nice app bandwidth rule for specific app
- *
- * @param uid uid of target app
- * @throws ServiceSpecificException in case of failure, with an error code indicating the
- * cause of the failure.
- */
- @RequiresApi(Build.VERSION_CODES.TIRAMISU)
- public void removeNiceApp(final int uid) {
- throwIfPreT("removeNiceApp is not available on pre-T devices");
-
- removeRule(uid, HAPPY_BOX_MATCH, "removeNiceApp");
- }
-
- /**
* Set target firewall child chain
*
* @param childChain target chain to enable
@@ -637,6 +579,7 @@
return BpfNetMapsUtils.getUidRule(sUidOwnerMap, childChain, uid);
}
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
private Set<Integer> getUidsMatchEnabled(final int childChain) throws ErrnoException {
final long match = getMatchByFirewallChain(childChain);
Set<Integer> uids = new ArraySet<>();
@@ -665,6 +608,7 @@
* @param childChain target chain
* @return Set of uids
*/
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
public Set<Integer> getUidsWithAllowRuleOnAllowListChain(final int childChain)
throws ErrnoException {
if (!isFirewallAllowList(childChain)) {
@@ -686,6 +630,7 @@
* @param childChain target chain
* @return Set of uids
*/
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
public Set<Integer> getUidsWithDenyRuleOnDenyListChain(final int childChain)
throws ErrnoException {
if (isFirewallAllowList(childChain)) {
@@ -918,25 +863,6 @@
}
}
- /**
- * Return whether the network is blocked by firewall chains for the given uid.
- *
- * Note that {@link #getDataSaverEnabled()} has a latency before V.
- *
- * @param uid The target uid.
- * @param isNetworkMetered Whether the target network is metered.
- *
- * @return True if the network is blocked. Otherwise, false.
- * @throws ServiceSpecificException if the read fails.
- *
- * @hide
- */
- @RequiresApi(Build.VERSION_CODES.TIRAMISU)
- public boolean isUidNetworkingBlocked(final int uid, boolean isNetworkMetered) {
- return BpfNetMapsUtils.isUidNetworkingBlocked(uid, isNetworkMetered,
- sConfigurationMap, sUidOwnerMap, sDataSaverEnabledMap);
- }
-
/** Register callback for statsd to pull atom. */
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
public void setPullAtomCallback(final Context context) {
@@ -999,6 +925,7 @@
return sj.toString();
}
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
private void dumpOwnerMatchConfig(final IndentingPrintWriter pw) {
try {
final long match = sConfigurationMap.getValue(UID_RULES_CONFIGURATION_KEY).val;
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index b1ae019..b99d0de 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -24,6 +24,8 @@
import static android.content.pm.PackageManager.FEATURE_WIFI;
import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.BpfNetMapsConstants.METERED_ALLOW_CHAINS;
+import static android.net.BpfNetMapsConstants.METERED_DENY_CHAINS;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_ATTEMPTED_BITMASK;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_SUCCEEDED_BITMASK;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_VALIDATION_RESULT;
@@ -2238,11 +2240,7 @@
final long ident = Binder.clearCallingIdentity();
try {
final boolean metered = nc == null ? true : nc.isMetered();
- if (mDeps.isAtLeastV()) {
- return mBpfNetMaps.isUidNetworkingBlocked(uid, metered);
- } else {
- return mPolicyManager.isUidNetworkingBlocked(uid, metered);
- }
+ return mPolicyManager.isUidNetworkingBlocked(uid, metered);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -12737,8 +12735,8 @@
if (um.isManagedProfile(profile.getIdentifier())) {
return true;
}
- if (mDeps.isAtLeastT() && dpm.getDeviceOwner() != null) return true;
- return false;
+
+ return mDeps.isAtLeastT() && dpm.getDeviceOwnerComponentOnAnyUser() != null;
}
/**
@@ -13478,36 +13476,6 @@
}
}
- @Override
- public void updateMeteredNetworkAllowList(final int uid, final boolean add) {
- enforceNetworkStackOrSettingsPermission();
-
- try {
- if (add) {
- mBpfNetMaps.addNiceApp(uid);
- } else {
- mBpfNetMaps.removeNiceApp(uid);
- }
- } catch (ServiceSpecificException e) {
- throw new IllegalStateException(e);
- }
- }
-
- @Override
- public void updateMeteredNetworkDenyList(final int uid, final boolean add) {
- enforceNetworkStackOrSettingsPermission();
-
- try {
- if (add) {
- mBpfNetMaps.addNaughtyApp(uid);
- } else {
- mBpfNetMaps.removeNaughtyApp(uid);
- }
- } catch (ServiceSpecificException e) {
- throw new IllegalStateException(e);
- }
- }
-
private int setPackageFirewallRule(final int chain, final String packageName, final int rule)
throws PackageManager.NameNotFoundException {
final PackageManager pm = mContext.getPackageManager();
@@ -13567,6 +13535,8 @@
case ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_1:
case ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_2:
case ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_3:
+ case ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER:
+ case ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN:
defaultRule = FIREWALL_RULE_ALLOW;
break;
case ConnectivityManager.FIREWALL_CHAIN_DOZABLE:
@@ -13574,6 +13544,7 @@
case ConnectivityManager.FIREWALL_CHAIN_RESTRICTED:
case ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY:
case ConnectivityManager.FIREWALL_CHAIN_BACKGROUND:
+ case ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW:
defaultRule = FIREWALL_RULE_DENY;
break;
default:
@@ -13584,6 +13555,7 @@
return rule;
}
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
private void closeSocketsForFirewallChainLocked(final int chain)
throws ErrnoException, SocketException, InterruptedIOException {
if (BpfNetMapsUtils.isFirewallAllowList(chain)) {
@@ -13610,6 +13582,12 @@
+ " the feature is disabled.");
return;
}
+ if (METERED_ALLOW_CHAINS.contains(chain) || METERED_DENY_CHAINS.contains(chain)) {
+ // Metered chains are used from a separate bpf program that is triggered by iptables
+ // and can not be controlled by setFirewallChainEnabled.
+ throw new UnsupportedOperationException(
+ "Chain (" + chain + ") can not be controlled by setFirewallChainEnabled");
+ }
try {
mBpfNetMaps.setChildChain(chain, enable);
@@ -13630,6 +13608,13 @@
public boolean getFirewallChainEnabled(final int chain) {
enforceNetworkStackOrSettingsPermission();
+ if (METERED_ALLOW_CHAINS.contains(chain) || METERED_DENY_CHAINS.contains(chain)) {
+ // Metered chains are used from a separate bpf program that is triggered by iptables
+ // and can not be controlled by setFirewallChainEnabled.
+ throw new UnsupportedOperationException(
+ "getFirewallChainEnabled can not return status of chain (" + chain + ")");
+ }
+
return mBpfNetMaps.isChainEnabled(chain);
}
diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java
index eea16bf..aec4f24 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -399,6 +399,10 @@
mCookieTagMap = mDeps.getBpfCookieTagMap();
}
+ // Note that this may only be called on a brand new v4-* interface,
+ // because it uses bpfmap.insertEntry() which fails if entry exists,
+ // and because the value includes (initialized to 0) byte/packet
+ // counters, so a replace (instead of insert) would wipe those stats.
private void maybeStartBpf(final ClatdTracker tracker) {
if (mIngressMap == null || mEgressMap == null) return;
diff --git a/service/src/com/android/server/connectivity/DscpPolicyTracker.java b/service/src/com/android/server/connectivity/DscpPolicyTracker.java
index 15d6adb..9c2b9e8 100644
--- a/service/src/com/android/server/connectivity/DscpPolicyTracker.java
+++ b/service/src/com/android/server/connectivity/DscpPolicyTracker.java
@@ -64,6 +64,8 @@
return "/sys/fs/bpf/net_shared/map_" + which + "_map";
}
+ private final boolean mHaveProgram = TcUtils.isBpfProgramUsable(PROG_PATH);
+
private Set<String> mAttachedIfaces;
private final BpfMap<Struct.S32, DscpPolicyValue> mBpfDscpIpv4Policies;
@@ -325,6 +327,7 @@
* Attach BPF program
*/
private boolean attachProgram(@NonNull String iface) {
+ if (!mHaveProgram) return false;
try {
NetworkInterface netIface = NetworkInterface.getByName(iface);
TcUtils.tcFilterAddDevBpf(netIface.getIndex(), false, PRIO_DSCP, (short) ETH_P_ALL,
diff --git a/service/src/com/android/server/connectivity/MulticastRoutingCoordinatorService.java b/service/src/com/android/server/connectivity/MulticastRoutingCoordinatorService.java
index 4d5001b..ac479b8 100644
--- a/service/src/com/android/server/connectivity/MulticastRoutingCoordinatorService.java
+++ b/service/src/com/android/server/connectivity/MulticastRoutingCoordinatorService.java
@@ -27,6 +27,8 @@
import static android.system.OsConstants.SOCK_NONBLOCK;
import static android.system.OsConstants.SOCK_RAW;
+import static com.android.net.module.util.CollectionUtils.getIndexForValue;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.MulticastRoutingConfig;
@@ -150,7 +152,7 @@
}
private Integer getInterfaceIndex(String ifName) {
- int mapIndex = mInterfaces.indexOfValue(ifName);
+ int mapIndex = getIndexForValue(mInterfaces, ifName);
if (mapIndex < 0) return null;
return mInterfaces.keyAt(mapIndex);
}
@@ -246,7 +248,7 @@
if (virtualIndex == null) return;
updateMfcs();
- mInterfaces.removeAt(mInterfaces.indexOfValue(ifName));
+ mInterfaces.removeAt(getIndexForValue(mInterfaces, ifName));
mVirtualInterfaces.remove(virtualIndex);
try {
mDependencies.setsockoptMrt6DelMif(mMulticastRoutingFd, virtualIndex);
@@ -270,7 +272,7 @@
@VisibleForTesting
public Integer getVirtualInterfaceIndex(String ifName) {
- int mapIndex = mVirtualInterfaces.indexOfValue(ifName);
+ int mapIndex = getIndexForValue(mVirtualInterfaces, ifName);
if (mapIndex < 0) return null;
return mVirtualInterfaces.keyAt(mapIndex);
}
@@ -291,7 +293,7 @@
private void maybeAddAndTrackInterface(String ifName) {
checkOnHandlerThread();
- if (mVirtualInterfaces.indexOfValue(ifName) >= 0) return;
+ if (getIndexForValue(mVirtualInterfaces, ifName) >= 0) return;
int nextVirtualIndex = getNextAvailableVirtualIndex();
int ifIndex = mDependencies.getInterfaceIndex(ifName);
diff --git a/staticlibs/device/com/android/net/module/util/TcUtils.java b/staticlibs/device/com/android/net/module/util/TcUtils.java
index 9d2fb7f..a6b222f 100644
--- a/staticlibs/device/com/android/net/module/util/TcUtils.java
+++ b/staticlibs/device/com/android/net/module/util/TcUtils.java
@@ -101,4 +101,12 @@
* @throws IOException
*/
public static native void tcQdiscAddDevClsact(int ifIndex) throws IOException;
+
+ /**
+ * Attempt to fetch a bpf program from a path.
+ * Return true on success, false on non-existence or any other failure.
+ *
+ * @param bpfProgPath
+ */
+ public static native boolean isBpfProgramUsable(String bpfProgPath);
}
diff --git a/staticlibs/device/com/android/net/module/util/netlink/NetlinkConstants.java b/staticlibs/device/com/android/net/module/util/netlink/NetlinkConstants.java
index ad7a4d7..1896de6 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/NetlinkConstants.java
+++ b/staticlibs/device/com/android/net/module/util/netlink/NetlinkConstants.java
@@ -123,6 +123,7 @@
public static final short RTM_NEWRULE = 32;
public static final short RTM_DELRULE = 33;
public static final short RTM_GETRULE = 34;
+ public static final short RTM_NEWPREFIX = 52;
public static final short RTM_NEWNDUSEROPT = 68;
// Netfilter netlink message types are presented by two bytes: high byte subsystem and
@@ -148,6 +149,8 @@
public static final int RTMGRP_IPV4_IFADDR = 0x10;
public static final int RTMGRP_IPV6_IFADDR = 0x100;
public static final int RTMGRP_IPV6_ROUTE = 0x400;
+ public static final int RTNLGRP_IPV6_PREFIX = 18;
+ public static final int RTMGRP_IPV6_PREFIX = 1 << (RTNLGRP_IPV6_PREFIX - 1);
public static final int RTNLGRP_ND_USEROPT = 20;
public static final int RTMGRP_ND_USEROPT = 1 << (RTNLGRP_ND_USEROPT - 1);
@@ -207,6 +210,7 @@
case RTM_NEWRULE: return "RTM_NEWRULE";
case RTM_DELRULE: return "RTM_DELRULE";
case RTM_GETRULE: return "RTM_GETRULE";
+ case RTM_NEWPREFIX: return "RTM_NEWPREFIX";
case RTM_NEWNDUSEROPT: return "RTM_NEWNDUSEROPT";
default: return "unknown RTM type: " + String.valueOf(nlmType);
}
diff --git a/staticlibs/device/com/android/net/module/util/netlink/StructPrefixCacheInfo.java b/staticlibs/device/com/android/net/module/util/netlink/StructPrefixCacheInfo.java
new file mode 100644
index 0000000..cfaa6e1
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/netlink/StructPrefixCacheInfo.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 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.net.module.util.netlink;
+
+import androidx.annotation.NonNull;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+import java.nio.ByteBuffer;
+
+/**
+ * struct prefix_cacheinfo {
+ * __u32 preferred_time;
+ * __u32 valid_time;
+ * }
+ *
+ * see also:
+ *
+ * include/uapi/linux/if_addr.h
+ *
+ * @hide
+ */
+public class StructPrefixCacheInfo extends Struct {
+ public static final int STRUCT_SIZE = 8;
+
+ @Field(order = 0, type = Type.U32)
+ public final long preferred_time;
+ @Field(order = 1, type = Type.U32)
+ public final long valid_time;
+
+ StructPrefixCacheInfo(long preferred, long valid) {
+ this.preferred_time = preferred;
+ this.valid_time = valid;
+ }
+
+ /**
+ * Parse a prefix_cacheinfo struct from a {@link ByteBuffer}.
+ *
+ * @param byteBuffer The buffer from which to parse the prefix_cacheinfo.
+ * @return the parsed prefix_cacheinfo struct, or throw IllegalArgumentException if the
+ * prefix_cacheinfo struct could not be parsed successfully(for example, if it was
+ * truncated).
+ */
+ public static StructPrefixCacheInfo parse(@NonNull final ByteBuffer byteBuffer) {
+ if (byteBuffer.remaining() < STRUCT_SIZE) {
+ throw new IllegalArgumentException("Invalid bytebuffer remaining size "
+ + byteBuffer.remaining() + " for prefix_cacheinfo attribute");
+ }
+
+ // The ByteOrder must already have been set to native order.
+ return Struct.parse(StructPrefixCacheInfo.class, byteBuffer);
+ }
+
+ /**
+ * Write a prefix_cacheinfo struct to {@link ByteBuffer}.
+ */
+ public void pack(@NonNull final ByteBuffer byteBuffer) {
+ // The ByteOrder must already have been set to native order.
+ writeToByteBuffer(byteBuffer);
+ }
+}
diff --git a/staticlibs/device/com/android/net/module/util/netlink/StructPrefixMsg.java b/staticlibs/device/com/android/net/module/util/netlink/StructPrefixMsg.java
new file mode 100644
index 0000000..504d6c7
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/netlink/StructPrefixMsg.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024 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.net.module.util.netlink;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+import java.nio.ByteBuffer;
+
+/**
+ * struct prefixmsg {
+ * unsigned char prefix_family;
+ * unsigned char prefix_pad1;
+ * unsigned short prefix_pad2;
+ * int prefix_ifindex;
+ * unsigned char prefix_type;
+ * unsigned char prefix_len;
+ * unsigned char prefix_flags;
+ * unsigned char prefix_pad3;
+ * }
+ *
+ * see also:
+ *
+ * include/uapi/linux/rtnetlink.h
+ *
+ * @hide
+ */
+public class StructPrefixMsg extends Struct {
+ // Already aligned.
+ public static final int STRUCT_SIZE = 12;
+
+ @Field(order = 0, type = Type.U8, padding = 3)
+ public final short prefix_family;
+ @Field(order = 1, type = Type.S32)
+ public final int prefix_ifindex;
+ @Field(order = 2, type = Type.U8)
+ public final short prefix_type;
+ @Field(order = 3, type = Type.U8)
+ public final short prefix_len;
+ @Field(order = 4, type = Type.U8, padding = 1)
+ public final short prefix_flags;
+
+ @VisibleForTesting
+ public StructPrefixMsg(short family, int ifindex, short type, short len, short flags) {
+ this.prefix_family = family;
+ this.prefix_ifindex = ifindex;
+ this.prefix_type = type;
+ this.prefix_len = len;
+ this.prefix_flags = flags;
+ }
+
+ /**
+ * Parse a prefixmsg struct from a {@link ByteBuffer}.
+ *
+ * @param byteBuffer The buffer from which to parse the prefixmsg.
+ * @return the parsed prefixmsg struct, or throw IllegalArgumentException if the prefixmsg
+ * struct could not be parsed successfully (for example, if it was truncated).
+ */
+ public static StructPrefixMsg parse(@NonNull final ByteBuffer byteBuffer) {
+ if (byteBuffer.remaining() < STRUCT_SIZE) {
+ throw new IllegalArgumentException("Invalid bytebuffer remaining size "
+ + byteBuffer.remaining() + "for prefix_msg struct.");
+ }
+
+ // The ByteOrder must already have been set to native order.
+ return Struct.parse(StructPrefixMsg.class, byteBuffer);
+ }
+
+ /**
+ * Write a prefixmsg struct to {@link ByteBuffer}.
+ */
+ public void pack(@NonNull final ByteBuffer byteBuffer) {
+ // The ByteOrder must already have been set to native order.
+ writeToByteBuffer(byteBuffer);
+ }
+}
diff --git a/staticlibs/framework/com/android/net/module/util/CollectionUtils.java b/staticlibs/framework/com/android/net/module/util/CollectionUtils.java
index 39e7ce9..f3d8c4a 100644
--- a/staticlibs/framework/com/android/net/module/util/CollectionUtils.java
+++ b/staticlibs/framework/com/android/net/module/util/CollectionUtils.java
@@ -389,4 +389,28 @@
}
return dest;
}
+
+ /**
+ * Returns an index of the given SparseArray that contains the given value, or -1
+ * number if no keys map to the given value.
+ *
+ * <p>Note this is a linear search, and if multiple keys can map to the same value
+ * then the smallest index is returned.
+ *
+ * <p>This function compares values with {@code equals} while the
+ * {@link SparseArray#indexOfValue} compares values using {@code ==}.
+ */
+ public static <T> int getIndexForValue(SparseArray<T> sparseArray, T value) {
+ for(int i = 0, nsize = sparseArray.size(); i < nsize; i++) {
+ T valueAt = sparseArray.valueAt(i);
+ if (valueAt == null) {
+ if (value == null) {
+ return i;
+ };
+ } else if (valueAt.equals(value)) {
+ return i;
+ }
+ }
+ return -1;
+ }
}
diff --git a/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java b/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
index 7c4abe0..19d8bbe 100644
--- a/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
+++ b/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
@@ -179,6 +179,7 @@
public static final int ICMPV6_RA_HEADER_LEN = 16;
public static final int ICMPV6_NS_HEADER_LEN = 24;
public static final int ICMPV6_NA_HEADER_LEN = 24;
+ public static final int ICMPV6_ND_OPTION_TLLA_LEN = 8;
public static final int NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER = 1 << 31;
public static final int NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED = 1 << 30;
diff --git a/staticlibs/native/bpf_headers/include/bpf/KernelUtils.h b/staticlibs/native/bpf_headers/include/bpf/KernelUtils.h
index 59257b8..417a5c4 100644
--- a/staticlibs/native/bpf_headers/include/bpf/KernelUtils.h
+++ b/staticlibs/native/bpf_headers/include/bpf/KernelUtils.h
@@ -42,10 +42,26 @@
return kver;
}
-static inline __unused bool isAtLeastKernelVersion(unsigned major, unsigned minor, unsigned sub) {
+static inline bool isAtLeastKernelVersion(unsigned major, unsigned minor, unsigned sub) {
return kernelVersion() >= KVER(major, minor, sub);
}
+static inline bool isKernelVersion(unsigned major, unsigned minor) {
+ return isAtLeastKernelVersion(major, minor, 0) && !isAtLeastKernelVersion(major, minor + 1, 0);
+}
+
+static inline bool __unused isLtsKernel() {
+ return isKernelVersion(4, 4) || // minimum for Android R
+ isKernelVersion(4, 9) || // minimum for Android S & T
+ isKernelVersion(4, 14) || // minimum for Android U
+ isKernelVersion(4, 19) || // minimum for Android V
+ isKernelVersion(5, 4) || // first supported in Android R
+ isKernelVersion(5, 10) || // first supported in Android S
+ isKernelVersion(5, 15) || // first supported in Android T
+ isKernelVersion(6, 1) || // first supported in Android U
+ isKernelVersion(6, 6); // first supported in Android V
+}
+
// Figure out the bitness of userspace.
// Trivial and known at compile time.
static constexpr bool isUserspace32bit() {
diff --git a/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h b/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h
index 9995cb9..cb02de8 100644
--- a/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h
+++ b/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h
@@ -148,6 +148,13 @@
return bpfFdGet(pathname, BPF_F_RDONLY);
}
+inline bool usableProgram(const char* pathname) {
+ int fd = retrieveProgram(pathname);
+ bool ok = (fd >= 0);
+ if (ok) close(fd);
+ return ok;
+}
+
inline int attachProgram(bpf_attach_type type, const BPF_FD_TYPE prog_fd,
const BPF_FD_TYPE cg_fd, uint32_t flags = 0) {
return bpf(BPF_PROG_ATTACH, {
diff --git a/staticlibs/native/bpfmapjni/com_android_net_module_util_TcUtils.cpp b/staticlibs/native/bpfmapjni/com_android_net_module_util_TcUtils.cpp
index ab83da6..2a587b6 100644
--- a/staticlibs/native/bpfmapjni/com_android_net_module_util_TcUtils.cpp
+++ b/staticlibs/native/bpfmapjni/com_android_net_module_util_TcUtils.cpp
@@ -19,6 +19,9 @@
#include <nativehelper/scoped_utf_chars.h>
#include <tcutils/tcutils.h>
+#define BPF_FD_JUST_USE_INT
+#include "BpfSyscallWrappers.h"
+
namespace android {
static void throwIOException(JNIEnv *env, const char *msg, int error) {
@@ -96,6 +99,14 @@
}
}
+static jboolean com_android_net_module_util_TcUtils_isBpfProgramUsable(JNIEnv *env,
+ jclass clazz,
+ jstring bpfProgPath) {
+ ScopedUtfChars pathname(env, bpfProgPath);
+ return bpf::usableProgram(pathname.c_str());
+}
+
+
/*
* JNI registration.
*/
@@ -111,6 +122,8 @@
(void *)com_android_net_module_util_TcUtils_tcFilterDelDev},
{"tcQdiscAddDevClsact", "(I)V",
(void *)com_android_net_module_util_TcUtils_tcQdiscAddDevClsact},
+ {"isBpfProgramUsable", "(Ljava/lang/String;)Z",
+ (void *)com_android_net_module_util_TcUtils_isBpfProgramUsable},
};
int register_com_android_net_module_util_TcUtils(JNIEnv *env,
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/CollectionUtilsTest.kt b/staticlibs/tests/unit/src/com/android/net/module/util/CollectionUtilsTest.kt
index e23f999..4ed3afd 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/CollectionUtilsTest.kt
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/CollectionUtilsTest.kt
@@ -16,6 +16,7 @@
package com.android.net.module.util
+import android.util.SparseArray
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.testutils.assertThrows
@@ -179,4 +180,20 @@
CollectionUtils.assoc(listOf(1, 2), list15)
}
}
+
+ @Test
+ fun testGetIndexForValue() {
+ val sparseArray = SparseArray<String>();
+ sparseArray.put(5, "hello");
+ sparseArray.put(10, "abcd");
+ sparseArray.put(20, null);
+
+ val value1 = "abcd";
+ val value1Copy = String(value1.toCharArray())
+ val value2 = null;
+
+ assertEquals(1, CollectionUtils.getIndexForValue(sparseArray, value1));
+ assertEquals(1, CollectionUtils.getIndexForValue(sparseArray, value1Copy));
+ assertEquals(2, CollectionUtils.getIndexForValue(sparseArray, value2));
+ }
}
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/NetlinkConstantsTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/NetlinkConstantsTest.java
index 143e4d4..e42c552 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/NetlinkConstantsTest.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/NetlinkConstantsTest.java
@@ -46,6 +46,7 @@
import static com.android.net.module.util.netlink.NetlinkConstants.RTM_NEWADDR;
import static com.android.net.module.util.netlink.NetlinkConstants.RTM_NEWLINK;
import static com.android.net.module.util.netlink.NetlinkConstants.RTM_NEWNDUSEROPT;
+import static com.android.net.module.util.netlink.NetlinkConstants.RTM_NEWPREFIX;
import static com.android.net.module.util.netlink.NetlinkConstants.RTM_NEWNEIGH;
import static com.android.net.module.util.netlink.NetlinkConstants.RTM_NEWROUTE;
import static com.android.net.module.util.netlink.NetlinkConstants.RTM_NEWRULE;
@@ -89,6 +90,7 @@
assertEquals("RTM_NEWRULE", stringForNlMsgType(RTM_NEWRULE, NETLINK_ROUTE));
assertEquals("RTM_DELRULE", stringForNlMsgType(RTM_DELRULE, NETLINK_ROUTE));
assertEquals("RTM_GETRULE", stringForNlMsgType(RTM_GETRULE, NETLINK_ROUTE));
+ assertEquals("RTM_NEWPREFIX", stringForNlMsgType(RTM_NEWPREFIX, NETLINK_ROUTE));
assertEquals("RTM_NEWNDUSEROPT", stringForNlMsgType(RTM_NEWNDUSEROPT, NETLINK_ROUTE));
assertEquals("SOCK_DIAG_BY_FAMILY",
diff --git a/staticlibs/testutils/Android.bp b/staticlibs/testutils/Android.bp
index 9124ac0..3843b90 100644
--- a/staticlibs/testutils/Android.bp
+++ b/staticlibs/testutils/Android.bp
@@ -99,6 +99,8 @@
"mcts-networking",
"mts-tethering",
"mcts-tethering",
+ "mcts-wifi",
+ "mcts-dnsresolver",
],
data: [":ConnectivityTestPreparer"],
}
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/AutoReleaseNetworkCallbackRule.kt b/staticlibs/testutils/devicetests/com/android/testutils/AutoReleaseNetworkCallbackRule.kt
index 28ae609..93422ad 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/AutoReleaseNetworkCallbackRule.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/AutoReleaseNetworkCallbackRule.kt
@@ -21,6 +21,7 @@
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
+import android.os.Handler
import androidx.test.platform.app.InstrumentationRegistry
import com.android.testutils.RecorderCallback.CallbackEntry
import java.util.Collections
@@ -97,6 +98,15 @@
cellRequestCb = null
}
+ private fun addCallback(
+ cb: TestableNetworkCallback,
+ registrar: (TestableNetworkCallback) -> Unit
+ ): TestableNetworkCallback {
+ registrar(cb)
+ cbToCleanup.add(cb)
+ return cb
+ }
+
/**
* File a request for a Network.
*
@@ -109,14 +119,27 @@
@JvmOverloads
fun requestNetwork(
request: NetworkRequest,
- cb: TestableNetworkCallback = TestableNetworkCallback()
- ): TestableNetworkCallback {
- cm.requestNetwork(request, cb)
- cbToCleanup.add(cb)
- return cb
+ cb: TestableNetworkCallback = TestableNetworkCallback(),
+ handler: Handler? = null
+ ) = addCallback(cb) {
+ if (handler == null) {
+ cm.requestNetwork(request, it)
+ } else {
+ cm.requestNetwork(request, it, handler)
+ }
}
/**
+ * Overload of [requestNetwork] that allows specifying a timeout.
+ */
+ @JvmOverloads
+ fun requestNetwork(
+ request: NetworkRequest,
+ cb: TestableNetworkCallback = TestableNetworkCallback(),
+ timeoutMs: Int,
+ ) = addCallback(cb) { cm.requestNetwork(request, it, timeoutMs) }
+
+ /**
* File a callback for a NetworkRequest.
*
* This will fail tests (throw) if the cell network cannot be obtained, or if it was already
@@ -129,13 +152,63 @@
fun registerNetworkCallback(
request: NetworkRequest,
cb: TestableNetworkCallback = TestableNetworkCallback()
- ): TestableNetworkCallback {
- cm.registerNetworkCallback(request, cb)
- cbToCleanup.add(cb)
- return cb
+ ) = addCallback(cb) { cm.registerNetworkCallback(request, it) }
+
+ /**
+ * @see ConnectivityManager.registerDefaultNetworkCallback
+ */
+ @JvmOverloads
+ fun registerDefaultNetworkCallback(
+ cb: TestableNetworkCallback = TestableNetworkCallback(),
+ handler: Handler? = null
+ ) = addCallback(cb) {
+ if (handler == null) {
+ cm.registerDefaultNetworkCallback(it)
+ } else {
+ cm.registerDefaultNetworkCallback(it, handler)
+ }
}
/**
+ * @see ConnectivityManager.registerSystemDefaultNetworkCallback
+ */
+ @JvmOverloads
+ fun registerSystemDefaultNetworkCallback(
+ cb: TestableNetworkCallback = TestableNetworkCallback(),
+ handler: Handler
+ ) = addCallback(cb) { cm.registerSystemDefaultNetworkCallback(it, handler) }
+
+ /**
+ * @see ConnectivityManager.registerDefaultNetworkCallbackForUid
+ */
+ @JvmOverloads
+ fun registerDefaultNetworkCallbackForUid(
+ uid: Int,
+ cb: TestableNetworkCallback = TestableNetworkCallback(),
+ handler: Handler
+ ) = addCallback(cb) { cm.registerDefaultNetworkCallbackForUid(uid, it, handler) }
+
+ /**
+ * @see ConnectivityManager.registerBestMatchingNetworkCallback
+ */
+ @JvmOverloads
+ fun registerBestMatchingNetworkCallback(
+ request: NetworkRequest,
+ cb: TestableNetworkCallback = TestableNetworkCallback(),
+ handler: Handler
+ ) = addCallback(cb) { cm.registerBestMatchingNetworkCallback(request, it, handler) }
+
+ /**
+ * @see ConnectivityManager.requestBackgroundNetwork
+ */
+ @JvmOverloads
+ fun requestBackgroundNetwork(
+ request: NetworkRequest,
+ cb: TestableNetworkCallback = TestableNetworkCallback(),
+ handler: Handler
+ ) = addCallback(cb) { cm.requestBackgroundNetwork(request, it, handler) }
+
+ /**
* Unregister a callback filed using registration methods in this class.
*/
fun unregisterNetworkCallback(cb: NetworkCallback) {
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/ConnectUtil.kt b/staticlibs/testutils/devicetests/com/android/testutils/ConnectUtil.kt
index 8090d5b..3857810 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/ConnectUtil.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/ConnectUtil.kt
@@ -86,6 +86,7 @@
val callback = TestableNetworkCallback(timeoutMs = WIFI_CONNECT_TIMEOUT_MS)
cm.registerNetworkCallback(NetworkRequest.Builder()
.addTransportType(TRANSPORT_WIFI)
+ .addCapability(NET_CAPABILITY_INTERNET)
.build(), callback)
return tryTest {
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/SetFeatureFlagsRule.kt b/staticlibs/testutils/devicetests/com/android/testutils/SetFeatureFlagsRule.kt
new file mode 100644
index 0000000..4185b05
--- /dev/null
+++ b/staticlibs/testutils/devicetests/com/android/testutils/SetFeatureFlagsRule.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2024 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.com.android.testutils
+
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * A JUnit Rule that sets feature flags based on `@FeatureFlag` annotations.
+ *
+ * This rule enables dynamic control of feature flag states during testing.
+ *
+ * **Usage:**
+ * ```kotlin
+ * class MyTestClass {
+ * @get:Rule
+ * val setFeatureFlagsRule = SetFeatureFlagsRule(setFlagsMethod = (name, enabled) -> {
+ * // Custom handling code.
+ * })
+ *
+ * // ... test methods with @FeatureFlag annotations
+ * @FeatureFlag("FooBar1", true)
+ * @FeatureFlag("FooBar2", false)
+ * @Test
+ * fun testFooBar() {}
+ * }
+ * ```
+ */
+class SetFeatureFlagsRule(val setFlagsMethod: (name: String, enabled: Boolean) -> Unit) : TestRule {
+ /**
+ * This annotation marks a test method as requiring a specific feature flag to be configured.
+ *
+ * Use this on test methods to dynamically control feature flag states during testing.
+ *
+ * @param name The name of the feature flag.
+ * @param enabled The desired state (true for enabled, false for disabled) of the feature flag.
+ */
+ @Target(AnnotationTarget.FUNCTION)
+ @Retention(AnnotationRetention.RUNTIME)
+ annotation class FeatureFlag(val name: String, val enabled: Boolean = true)
+
+ /**
+ * This method is the core of the rule, executed by the JUnit framework before each test method.
+ *
+ * It retrieves the test method's metadata.
+ * If any `@FeatureFlag` annotation is found, it passes every feature flag's name
+ * and enabled state into the user-specified lambda to apply custom actions.
+ */
+ override fun apply(base: Statement, description: Description): Statement {
+ return object : Statement() {
+ override fun evaluate() {
+ val testMethod = description.testClass.getMethod(description.methodName)
+ val featureFlagAnnotations = testMethod.getAnnotationsByType(
+ FeatureFlag::class.java
+ )
+
+ for (featureFlagAnnotation in featureFlagAnnotations) {
+ setFlagsMethod(featureFlagAnnotation.name, featureFlagAnnotation.enabled)
+ }
+
+ // Execute the test method, which includes methods annotated with
+ // @Before, @Test and @After.
+ base.evaluate()
+ }
+ }
+ }
+}
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt b/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt
index 05c0444..f76916a 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt
@@ -91,6 +91,7 @@
override val network: Network,
val reason: Int
) : CallbackEntry()
+
// Convenience constants for expecting a type
companion object {
@JvmField
@@ -216,7 +217,11 @@
) : this(null, timeoutMs, noCallbackTimeoutMs, waiterFunc)
fun createLinkedCopy() = TestableNetworkCallback(
- this, defaultTimeoutMs, defaultNoCallbackTimeoutMs, waiterFunc)
+ 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
@@ -402,8 +407,11 @@
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()}")
+ assertNotNull(
+ it,
+ "Callback ${T::class} not received within ${timeoutMs}ms. " +
+ "Got ${history.backtrace()}"
+ )
} as T
@JvmOverloads
@@ -412,8 +420,11 @@
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()}")
+ assertNotNull(
+ it,
+ "Callback ${type.java} not received within ${timeoutMs}ms. " +
+ "Got ${history.backtrace()}"
+ )
} as T
fun <T : CallbackEntry> eventuallyExpect(
@@ -422,8 +433,11 @@
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()}")
+ 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:
@@ -534,8 +548,13 @@
blockedReason: Int,
tmt: Long = defaultTimeoutMs
) {
- expectAvailableCallbacks(net, validated = false, suspended = false,
- blockedReason = blockedReason, tmt = tmt)
+ expectAvailableCallbacks(
+ net,
+ validated = false,
+ suspended = false,
+ blockedReason = blockedReason,
+ tmt = tmt
+ )
expectCaps(net, tmt) { it.hasCapability(NET_CAPABILITY_VALIDATED) }
}
diff --git a/tests/common/java/android/net/nsd/NsdServiceInfoTest.java b/tests/common/java/android/net/nsd/NsdServiceInfoTest.java
index 8e89037..21e34ab 100644
--- a/tests/common/java/android/net/nsd/NsdServiceInfoTest.java
+++ b/tests/common/java/android/net/nsd/NsdServiceInfoTest.java
@@ -16,6 +16,7 @@
package android.net.nsd;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
@@ -51,6 +52,23 @@
private static final InetAddress IPV4_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.1");
private static final InetAddress IPV6_ADDRESS = InetAddresses.parseNumericAddress("2001:db8::");
+ private static final byte[] PUBLIC_KEY_RDATA = new byte[] {
+ (byte) 0x02, (byte)0x01, // flag
+ (byte) 0x03, // protocol
+ (byte) 0x0d, // algorithm
+ // 64-byte public key below
+ (byte) 0xC1, (byte) 0x41, (byte) 0xD0, (byte) 0x63, (byte) 0x79, (byte) 0x60,
+ (byte) 0xB9, (byte) 0x8C, (byte) 0xBC, (byte) 0x12, (byte) 0xCF, (byte) 0xCA,
+ (byte) 0x22, (byte) 0x1D, (byte) 0x28, (byte) 0x79, (byte) 0xDA, (byte) 0xC2,
+ (byte) 0x6E, (byte) 0xE5, (byte) 0xB4, (byte) 0x60, (byte) 0xE9, (byte) 0x00,
+ (byte) 0x7C, (byte) 0x99, (byte) 0x2E, (byte) 0x19, (byte) 0x02, (byte) 0xD8,
+ (byte) 0x97, (byte) 0xC3, (byte) 0x91, (byte) 0xB0, (byte) 0x37, (byte) 0x64,
+ (byte) 0xD4, (byte) 0x48, (byte) 0xF7, (byte) 0xD0, (byte) 0xC7, (byte) 0x72,
+ (byte) 0xFD, (byte) 0xB0, (byte) 0x3B, (byte) 0x1D, (byte) 0x9D, (byte) 0x6D,
+ (byte) 0x52, (byte) 0xFF, (byte) 0x88, (byte) 0x86, (byte) 0x76, (byte) 0x9E,
+ (byte) 0x8E, (byte) 0x23, (byte) 0x62, (byte) 0x51, (byte) 0x35, (byte) 0x65,
+ (byte) 0x27, (byte) 0x09, (byte) 0x62, (byte) 0xD3
+ };
@Test
public void testLimits() throws Exception {
@@ -120,6 +138,7 @@
fullInfo.setPort(4242);
fullInfo.setHostAddresses(List.of(IPV4_ADDRESS));
fullInfo.setHostname("home");
+ fullInfo.setPublicKey(PUBLIC_KEY_RDATA);
fullInfo.setNetwork(new Network(123));
fullInfo.setInterfaceIndex(456);
checkParcelable(fullInfo);
@@ -136,6 +155,7 @@
attributedInfo.setPort(4242);
attributedInfo.setHostAddresses(List.of(IPV6_ADDRESS, IPV4_ADDRESS));
attributedInfo.setHostname("home");
+ attributedInfo.setPublicKey(PUBLIC_KEY_RDATA);
attributedInfo.setAttribute("color", "pink");
attributedInfo.setAttribute("sound", (new String("にゃあ")).getBytes("UTF-8"));
attributedInfo.setAttribute("adorable", (String) null);
@@ -172,6 +192,7 @@
assertEquals(original.getServiceType(), result.getServiceType());
assertEquals(original.getHost(), result.getHost());
assertEquals(original.getHostname(), result.getHostname());
+ assertArrayEquals(original.getPublicKey(), result.getPublicKey());
assertTrue(original.getPort() == result.getPort());
assertEquals(original.getNetwork(), result.getNetwork());
assertEquals(original.getInterfaceIndex(), result.getInterfaceIndex());
diff --git a/tests/cts/hostside-network-policy/Android.bp b/tests/cts/hostside-network-policy/Android.bp
new file mode 100644
index 0000000..c3ce0b9
--- /dev/null
+++ b/tests/cts/hostside-network-policy/Android.bp
@@ -0,0 +1,46 @@
+// Copyright (C) 2024 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 {
+ default_team: "trendy_team_framework_backstage_power",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_test_host {
+ name: "CtsHostsideNetworkPolicyTests",
+ defaults: ["cts_defaults"],
+ // Only compile source java files in this apk.
+ srcs: [
+ "src/**/*.java",
+ ":ArgumentConstants",
+ ],
+ libs: [
+ "cts-tradefed",
+ "tradefed",
+ ],
+ static_libs: [
+ "modules-utils-build-testing",
+ ],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "sts",
+ ],
+ data: [
+ ":CtsHostsideNetworkPolicyTestsApp",
+ ":CtsHostsideNetworkPolicyTestsApp2",
+ ],
+ per_testcase_directory: true,
+}
diff --git a/tests/cts/hostside-network-policy/AndroidTest.xml b/tests/cts/hostside-network-policy/AndroidTest.xml
new file mode 100644
index 0000000..44f77f8
--- /dev/null
+++ b/tests/cts/hostside-network-policy/AndroidTest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 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.
+-->
+<configuration description="Config for CTS network policy host test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="networking" />
+ <option name="config-descriptor:metadata" key="token" value="SIM_CARD" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
+ <target_preparer class="com.android.cts.netpolicy.NetworkPolicyTestsPreparer" />
+
+ <!-- Enabling change id ALLOW_TEST_API_ACCESS allows that package to access @TestApi methods -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="am compat enable ALLOW_TEST_API_ACCESS com.android.cts.netpolicy.hostside.app2" />
+ <option name="teardown-command" value="am compat reset ALLOW_TEST_API_ACCESS com.android.cts.netpolicy.hostside.app2" />
+ <option name="teardown-command" value="cmd power set-mode 0" />
+ <option name="teardown-command" value="cmd battery reset" />
+ <option name="teardown-command" value="cmd netpolicy stop-watching" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="force-skip-system-props" value="true" />
+ <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
+ <option name="set-global-setting" key="low_power_standby_enabled" value="0" />
+ </target_preparer>
+
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="CtsHostsideNetworkPolicyTests.jar" />
+ <option name="runtime-hint" value="3m56s" />
+ </test>
+
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys" value="/sdcard/CtsHostsideNetworkPolicyTests" />
+ <option name="collect-on-run-ended-only" value="true" />
+ </metrics_collector>
+</configuration>
diff --git a/tests/cts/hostside-network-policy/OWNERS b/tests/cts/hostside-network-policy/OWNERS
new file mode 100644
index 0000000..ea83e61
--- /dev/null
+++ b/tests/cts/hostside-network-policy/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 61373
+# Inherits parent owners
+include platform/frameworks/base:/services/core/java/com/android/server/net/OWNERS
diff --git a/tests/cts/hostside-network-policy/TEST_MAPPING b/tests/cts/hostside-network-policy/TEST_MAPPING
new file mode 100644
index 0000000..57ac4f7
--- /dev/null
+++ b/tests/cts/hostside-network-policy/TEST_MAPPING
@@ -0,0 +1,27 @@
+{
+ "presubmit-large": [
+ {
+ "name": "CtsHostsideNetworkPolicyTests",
+ "options": [
+ {
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.RequiresDevice"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ // Postsubmit on virtual devices to monitor flakiness of all tests that don't require a
+ // physical device
+ "name": "CtsHostsideNetworkPolicyTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.RequiresDevice"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/cts/hostside-network-policy/aidl/Android.bp b/tests/cts/hostside-network-policy/aidl/Android.bp
new file mode 100644
index 0000000..b182090
--- /dev/null
+++ b/tests/cts/hostside-network-policy/aidl/Android.bp
@@ -0,0 +1,26 @@
+// Copyright (C) 2024 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 {
+ default_team: "trendy_team_framework_backstage_power",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_test_helper_library {
+ name: "CtsHostsideNetworkPolicyTestsAidl",
+ sdk_version: "current",
+ srcs: [
+ "com/android/cts/netpolicy/hostside/*.aidl",
+ ],
+}
diff --git a/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl b/tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/IMyService.aidl
similarity index 86%
rename from tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl
rename to tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/IMyService.aidl
index 906024b..068d9d8 100644
--- a/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl
+++ b/tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/IMyService.aidl
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
import android.app.job.JobInfo;
-import com.android.cts.net.hostside.INetworkCallback;
-import com.android.cts.net.hostside.NetworkCheckResult;
+import com.android.cts.netpolicy.hostside.INetworkCallback;
+import com.android.cts.netpolicy.hostside.NetworkCheckResult;
interface IMyService {
void registerBroadcastReceiver();
diff --git a/tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkCallback.aidl b/tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/INetworkCallback.aidl
similarity index 95%
rename from tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkCallback.aidl
rename to tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/INetworkCallback.aidl
index 2048bab..38efc7b 100644
--- a/tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkCallback.aidl
+++ b/tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/INetworkCallback.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
import android.net.Network;
import android.net.NetworkCapabilities;
diff --git a/tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkStateObserver.aidl b/tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/INetworkStateObserver.aidl
similarity index 89%
rename from tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkStateObserver.aidl
rename to tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/INetworkStateObserver.aidl
index 8ef4659..c6b7a1c 100644
--- a/tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkStateObserver.aidl
+++ b/tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/INetworkStateObserver.aidl
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
import android.net.NetworkInfo;
-import com.android.cts.net.hostside.NetworkCheckResult;
+import com.android.cts.netpolicy.hostside.NetworkCheckResult;
interface INetworkStateObserver {
void onNetworkStateChecked(int resultCode, in NetworkCheckResult networkCheckResult);
diff --git a/tests/cts/hostside/aidl/com/android/cts/net/hostside/NetworkCheckResult.aidl b/tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/NetworkCheckResult.aidl
similarity index 94%
rename from tests/cts/hostside/aidl/com/android/cts/net/hostside/NetworkCheckResult.aidl
rename to tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/NetworkCheckResult.aidl
index cdd6b70..7aac2ab 100644
--- a/tests/cts/hostside/aidl/com/android/cts/net/hostside/NetworkCheckResult.aidl
+++ b/tests/cts/hostside-network-policy/aidl/com/android/cts/netpolicy/hostside/NetworkCheckResult.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
import android.net.NetworkInfo;
diff --git a/tests/cts/hostside-network-policy/app/Android.bp b/tests/cts/hostside-network-policy/app/Android.bp
new file mode 100644
index 0000000..a31c843
--- /dev/null
+++ b/tests/cts/hostside-network-policy/app/Android.bp
@@ -0,0 +1,57 @@
+//
+// Copyright (C) 2024 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 {
+ default_team: "trendy_team_framework_backstage_power",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_defaults {
+ name: "CtsHostsideNetworkPolicyTestsAppDefaults",
+ platform_apis: true,
+ static_libs: [
+ "CtsHostsideNetworkPolicyTestsAidl",
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "androidx.test.uiautomator_uiautomator",
+ "compatibility-device-util-axt",
+ "cts-net-utils",
+ "ctstestrunner-axt",
+ "modules-utils-build",
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ":ArgumentConstants",
+ ],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "general-tests",
+ "sts",
+ ],
+}
+
+android_test_helper_app {
+ name: "CtsHostsideNetworkPolicyTestsApp",
+ defaults: [
+ "cts_support_defaults",
+ "framework-connectivity-test-defaults",
+ "CtsHostsideNetworkPolicyTestsAppDefaults",
+ ],
+}
diff --git a/tests/cts/hostside-network-policy/app/AndroidManifest.xml b/tests/cts/hostside-network-policy/app/AndroidManifest.xml
new file mode 100644
index 0000000..f19e35f
--- /dev/null
+++ b/tests/cts/hostside-network-policy/app/AndroidManifest.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.netpolicy.hostside">
+
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+ <application android:requestLegacyExternalStorage="true">
+ <uses-library android:name="android.test.runner"/>
+ <service android:name=".MyNotificationListenerService"
+ android:label="MyNotificationListenerService"
+ android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.service.notification.NotificationListenerService"/>
+ </intent-filter>
+ </service>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.netpolicy.hostside"/>
+
+</manifest>
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractAppIdleTestCase.java
similarity index 96%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractAppIdleTestCase.java
index d9ff539..19e4364 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractAppIdleTestCase.java
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
-import static com.android.cts.net.hostside.Property.APP_STANDBY_MODE;
-import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.Property.APP_STANDBY_MODE;
+import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE;
import static org.junit.Assert.assertEquals;
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractBatterySaverModeTestCase.java
similarity index 96%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractBatterySaverModeTestCase.java
index 0d7365f..ae226e2 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractBatterySaverModeTestCase.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
-import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE;
import org.junit.After;
import org.junit.Before;
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDefaultRestrictionsTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDefaultRestrictionsTest.java
similarity index 98%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDefaultRestrictionsTest.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDefaultRestrictionsTest.java
index 8a3e790..da633c0 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDefaultRestrictionsTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDefaultRestrictionsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING;
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDozeModeTestCase.java
similarity index 95%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDozeModeTestCase.java
index b037953..0c8cb70 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractDozeModeTestCase.java
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-import static com.android.cts.net.hostside.Property.DOZE_MODE;
-import static com.android.cts.net.hostside.Property.NOT_LOW_RAM_DEVICE;
+import static com.android.cts.netpolicy.hostside.Property.DOZE_MODE;
+import static com.android.cts.netpolicy.hostside.Property.NOT_LOW_RAM_DEVICE;
import android.os.SystemClock;
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractExpeditedJobTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractExpeditedJobTest.java
similarity index 88%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractExpeditedJobTest.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractExpeditedJobTest.java
index 7cac2af..5435920 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractExpeditedJobTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractExpeditedJobTest.java
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
-import static com.android.cts.net.hostside.Property.APP_STANDBY_MODE;
-import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
-import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
-import static com.android.cts.net.hostside.Property.DOZE_MODE;
-import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.netpolicy.hostside.Property.APP_STANDBY_MODE;
+import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.Property.DOZE_MODE;
+import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
import org.junit.After;
import org.junit.Before;
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractRestrictBackgroundNetworkTestCase.java
similarity index 96%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index 4437986..d0203c5 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
@@ -23,18 +23,18 @@
import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
import static android.os.BatteryManager.BATTERY_PLUGGED_ANY;
-import static com.android.cts.net.arguments.InstrumentationArguments.ARG_CONNECTION_CHECK_CUSTOM_URL;
-import static com.android.cts.net.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.executeShellCommand;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.forceRunJob;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getConnectivityManager;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getContext;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getInstrumentation;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isAppStandbySupported;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isBatterySaverSupported;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isDozeModeSupported;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.restrictBackgroundValueToString;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackgroundInternal;
+import static com.android.cts.netpolicy.arguments.InstrumentationArguments.ARG_CONNECTION_CHECK_CUSTOM_URL;
+import static com.android.cts.netpolicy.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.executeShellCommand;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.forceRunJob;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.getConnectivityManager;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.getContext;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.getInstrumentation;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isAppStandbySupported;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isBatterySaverSupported;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isDozeModeSupported;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.restrictBackgroundValueToString;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackgroundInternal;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -93,8 +93,8 @@
public abstract class AbstractRestrictBackgroundNetworkTestCase {
public static final String TAG = "RestrictBackgroundNetworkTests";
- protected static final String TEST_PKG = "com.android.cts.net.hostside";
- protected static final String TEST_APP2_PKG = "com.android.cts.net.hostside.app2";
+ protected static final String TEST_PKG = "com.android.cts.netpolicy.hostside";
+ protected static final String TEST_APP2_PKG = "com.android.cts.netpolicy.hostside.app2";
// TODO(b/321797685): Configure it via device-config once it is available.
protected static final long PROCESS_STATE_TRANSITION_DELAY_MS = TimeUnit.SECONDS.toMillis(5);
@@ -112,17 +112,17 @@
private static final String MANIFEST_RECEIVER = "ManifestReceiver";
private static final String DYNAMIC_RECEIVER = "DynamicReceiver";
private static final String ACTION_FINISH_ACTIVITY =
- "com.android.cts.net.hostside.app2.action.FINISH_ACTIVITY";
+ "com.android.cts.netpolicy.hostside.app2.action.FINISH_ACTIVITY";
private static final String ACTION_FINISH_JOB =
- "com.android.cts.net.hostside.app2.action.FINISH_JOB";
+ "com.android.cts.netpolicy.hostside.app2.action.FINISH_JOB";
// Copied from com.android.server.net.NetworkPolicyManagerService class
private static final String ACTION_SNOOZE_WARNING =
"com.android.server.net.action.SNOOZE_WARNING";
private static final String ACTION_RECEIVER_READY =
- "com.android.cts.net.hostside.app2.action.RECEIVER_READY";
+ "com.android.cts.netpolicy.hostside.app2.action.RECEIVER_READY";
static final String ACTION_SHOW_TOAST =
- "com.android.cts.net.hostside.app2.action.SHOW_TOAST";
+ "com.android.cts.netpolicy.hostside.app2.action.SHOW_TOAST";
protected static final String NOTIFICATION_TYPE_CONTENT = "CONTENT";
protected static final String NOTIFICATION_TYPE_DELETE = "DELETE";
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AppIdleMeteredTest.java
similarity index 85%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AppIdleMeteredTest.java
index f1858d6..6b802f6 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AppIdleMeteredTest.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
-import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
@RequiredProperties({METERED_NETWORK})
public class AppIdleMeteredTest extends AbstractAppIdleTestCase {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AppIdleNonMeteredTest.java
similarity index 85%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AppIdleNonMeteredTest.java
index e737a6d..2e725ae 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/AppIdleNonMeteredTest.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
-import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
@RequiredProperties({NON_METERED_NETWORK})
public class AppIdleNonMeteredTest extends AbstractAppIdleTestCase {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/BatterySaverModeMeteredTest.java
similarity index 85%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/BatterySaverModeMeteredTest.java
index c78ca2e..2e421f6 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/BatterySaverModeMeteredTest.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
-import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
@RequiredProperties({METERED_NETWORK})
public class BatterySaverModeMeteredTest extends AbstractBatterySaverModeTestCase {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/BatterySaverModeNonMeteredTest.java
similarity index 85%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/BatterySaverModeNonMeteredTest.java
index fb52a54..0be5644 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/BatterySaverModeNonMeteredTest.java
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
-import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
@RequiredProperties({NON_METERED_NETWORK})
public class BatterySaverModeNonMeteredTest extends AbstractBatterySaverModeTestCase {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ConnOnActivityStartTest.java
similarity index 85%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ConnOnActivityStartTest.java
index 3e22a23..811190f 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ConnOnActivityStartTest.java
@@ -14,20 +14,20 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getUiDevice;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
-import static com.android.cts.net.hostside.Property.APP_STANDBY_MODE;
-import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
-import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
-import static com.android.cts.net.hostside.Property.DOZE_MODE;
-import static com.android.cts.net.hostside.Property.METERED_NETWORK;
-import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.getUiDevice;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.netpolicy.hostside.Property.APP_STANDBY_MODE;
+import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.Property.DOZE_MODE;
+import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
import static org.junit.Assume.assumeTrue;
@@ -53,6 +53,7 @@
@After
public final void tearDown() throws Exception {
super.tearDown();
+ finishActivity();
resetDeviceState();
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DataSaverModeTest.java
similarity index 95%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DataSaverModeTest.java
index 790e031..66e0d00 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DataSaverModeTest.java
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
import static com.android.compatibility.common.util.FeatureUtil.isTV;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
-import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
-import static com.android.cts.net.hostside.Property.METERED_NETWORK;
-import static com.android.cts.net.hostside.Property.NO_DATA_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.netpolicy.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.Property.NO_DATA_SAVER_MODE;
import static org.junit.Assert.fail;
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataWarningReceiverTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DataWarningReceiverTest.java
similarity index 96%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/DataWarningReceiverTest.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DataWarningReceiverTest.java
index 13bbab6..69ca206 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataWarningReceiverTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DataWarningReceiverTest.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.clearSnoozeTimestamps;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.clearSnoozeTimestamps;
import android.content.pm.PackageManager;
import android.telephony.SubscriptionManager;
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DefaultRestrictionsMeteredTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DefaultRestrictionsMeteredTest.java
similarity index 86%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/DefaultRestrictionsMeteredTest.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DefaultRestrictionsMeteredTest.java
index f3a1026..810fd19 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DefaultRestrictionsMeteredTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DefaultRestrictionsMeteredTest.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
-import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
@RequiredProperties({METERED_NETWORK})
public class DefaultRestrictionsMeteredTest extends AbstractDefaultRestrictionsTest {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DefaultRestrictionsNonMeteredTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DefaultRestrictionsNonMeteredTest.java
similarity index 85%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/DefaultRestrictionsNonMeteredTest.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DefaultRestrictionsNonMeteredTest.java
index 5651dd0..fef546c 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DefaultRestrictionsNonMeteredTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DefaultRestrictionsNonMeteredTest.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
-import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
@RequiredProperties({NON_METERED_NETWORK})
public class DefaultRestrictionsNonMeteredTest extends AbstractDefaultRestrictionsTest {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DozeModeMeteredTest.java
similarity index 85%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DozeModeMeteredTest.java
index 4306c99..741dd7e 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DozeModeMeteredTest.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
-import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
@RequiredProperties({METERED_NETWORK})
public class DozeModeMeteredTest extends AbstractDozeModeTestCase {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DozeModeNonMeteredTest.java
similarity index 85%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DozeModeNonMeteredTest.java
index 1e89f15..f343df5 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DozeModeNonMeteredTest.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
-import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
@RequiredProperties({NON_METERED_NETWORK})
public class DozeModeNonMeteredTest extends AbstractDozeModeTestCase {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DumpOnFailureRule.java
similarity index 92%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DumpOnFailureRule.java
index 07434b1..2dc6cc4 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/DumpOnFailureRule.java
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
-import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
-import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TEST_APP2_PKG;
-import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TEST_PKG;
+import static com.android.cts.netpolicy.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
+import static com.android.cts.netpolicy.hostside.AbstractRestrictBackgroundNetworkTestCase.TEST_APP2_PKG;
+import static com.android.cts.netpolicy.hostside.AbstractRestrictBackgroundNetworkTestCase.TEST_PKG;
import android.os.Environment;
import android.os.FileUtils;
@@ -42,7 +42,7 @@
public class DumpOnFailureRule extends OnFailureRule {
private File mDumpDir = new File(Environment.getExternalStorageDirectory(),
- "CtsHostsideNetworkTests");
+ "CtsHostsideNetworkPolicyTests");
@Override
public void onTestFailure(Statement base, Description description, Throwable throwable) {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/ExpeditedJobMeteredTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ExpeditedJobMeteredTest.java
similarity index 85%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/ExpeditedJobMeteredTest.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ExpeditedJobMeteredTest.java
index 3809534..d56a50b 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/ExpeditedJobMeteredTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ExpeditedJobMeteredTest.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
-import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
@RequiredProperties({METERED_NETWORK})
public class ExpeditedJobMeteredTest extends AbstractExpeditedJobTest {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/ExpeditedJobNonMeteredTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ExpeditedJobNonMeteredTest.java
similarity index 85%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/ExpeditedJobNonMeteredTest.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ExpeditedJobNonMeteredTest.java
index 6596269..0a776ee 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/ExpeditedJobNonMeteredTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/ExpeditedJobNonMeteredTest.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
-import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
@RequiredProperties({NON_METERED_NETWORK})
public class ExpeditedJobNonMeteredTest extends AbstractExpeditedJobTest {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MeterednessConfigurationRule.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MeterednessConfigurationRule.java
similarity index 86%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/MeterednessConfigurationRule.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MeterednessConfigurationRule.java
index 5c99c67..4f4e68e 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MeterednessConfigurationRule.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MeterednessConfigurationRule.java
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setupActiveNetworkMeteredness;
-import static com.android.cts.net.hostside.Property.METERED_NETWORK;
-import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setupActiveNetworkMeteredness;
+import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
import android.util.ArraySet;
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MixedModesTest.java
similarity index 95%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MixedModesTest.java
index c9edda6..b0fa106 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MixedModesTest.java
@@ -13,15 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
-import static com.android.cts.net.hostside.Property.APP_STANDBY_MODE;
-import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
-import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
-import static com.android.cts.net.hostside.Property.DOZE_MODE;
-import static com.android.cts.net.hostside.Property.METERED_NETWORK;
-import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.netpolicy.hostside.Property.APP_STANDBY_MODE;
+import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.Property.DOZE_MODE;
+import static com.android.cts.netpolicy.hostside.Property.METERED_NETWORK;
+import static com.android.cts.netpolicy.hostside.Property.NON_METERED_NETWORK;
import android.os.SystemClock;
import android.util.Log;
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyNotificationListenerService.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MyNotificationListenerService.java
similarity index 98%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/MyNotificationListenerService.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MyNotificationListenerService.java
index 0132536..6dc9921 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyNotificationListenerService.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MyNotificationListenerService.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
import android.app.Notification;
import android.app.PendingIntent;
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MyServiceClient.java
similarity index 98%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MyServiceClient.java
index 494192f..71b28f6 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/MyServiceClient.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
import android.app.job.JobInfo;
import android.content.ComponentName;
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkCallbackTest.java
similarity index 96%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkCallbackTest.java
index 5552b8f..7038d02 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkCallbackTest.java
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.SIGNAL_STRENGTH_UNSPECIFIED;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.canChangeActiveNetworkMeteredness;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getActiveNetworkCapabilities;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
-import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
-import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.canChangeActiveNetworkMeteredness;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.getActiveNetworkCapabilities;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.Property.DATA_SAVER_MODE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyManagerTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyManagerTest.java
similarity index 95%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyManagerTest.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyManagerTest.java
index 968e270..9b3fe9f 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyManagerTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyManagerTest.java
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING;
import static android.os.Process.SYSTEM_UID;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.assertIsUidRestrictedOnMeteredNetworks;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.assertNetworkingBlockedStatusForUid;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isUidNetworkingBlocked;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
-import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
-import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.assertIsUidRestrictedOnMeteredNetworks;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.assertNetworkingBlockedStatusForUid;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isUidNetworkingBlocked;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.netpolicy.hostside.Property.DATA_SAVER_MODE;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestRunner.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyTestRunner.java
similarity index 96%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestRunner.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyTestRunner.java
index f340907..0207b00 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestRunner.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyTestRunner.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyTestUtils.java
similarity index 98%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyTestUtils.java
index 5331601..26a88f2 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/NetworkPolicyTestUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
@@ -26,7 +26,7 @@
import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_NONE;
import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
-import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
+import static com.android.cts.netpolicy.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/Property.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/Property.java
similarity index 72%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/Property.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/Property.java
index 18805f9..a03833f 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/Property.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/Property.java
@@ -13,15 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.canChangeActiveNetworkMeteredness;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isActiveNetworkMetered;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isAppStandbySupported;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isBatterySaverSupported;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isDataSaverSupported;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isDozeModeSupported;
-import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isLowRamDevice;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.canChangeActiveNetworkMeteredness;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isActiveNetworkMetered;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isAppStandbySupported;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isBatterySaverSupported;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isDataSaverSupported;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isDozeModeSupported;
+import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.isLowRamDevice;
public enum Property {
BATTERY_SAVER_MODE(1 << 0) {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/RequiredProperties.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/RequiredProperties.java
similarity index 95%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/RequiredProperties.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/RequiredProperties.java
index 96838bb..799a513 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/RequiredProperties.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/RequiredProperties.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/RequiredPropertiesRule.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/RequiredPropertiesRule.java
similarity index 95%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/RequiredPropertiesRule.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/RequiredPropertiesRule.java
index 01f9f3e..5dea67c 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/RequiredPropertiesRule.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/RequiredPropertiesRule.java
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
-import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
+import static com.android.cts.netpolicy.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
import android.text.TextUtils;
import android.util.ArraySet;
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/RestrictedModeTest.java b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/RestrictedModeTest.java
similarity index 97%
rename from tests/cts/hostside/app/src/com/android/cts/net/hostside/RestrictedModeTest.java
rename to tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/RestrictedModeTest.java
index 4777bf4..f183f4e 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/RestrictedModeTest.java
+++ b/tests/cts/hostside-network-policy/app/src/com/android/cts/netpolicy/hostside/RestrictedModeTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside;
+package com.android.cts.netpolicy.hostside;
import org.junit.After;
import org.junit.Before;
diff --git a/tests/cts/hostside-network-policy/app2/Android.bp b/tests/cts/hostside-network-policy/app2/Android.bp
new file mode 100644
index 0000000..6ef0b06
--- /dev/null
+++ b/tests/cts/hostside-network-policy/app2/Android.bp
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2024 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 {
+ default_team: "trendy_team_framework_backstage_power",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsHostsideNetworkPolicyTestsApp2",
+ defaults: ["cts_support_defaults"],
+ platform_apis: true,
+ static_libs: [
+ "androidx.annotation_annotation",
+ "CtsHostsideNetworkPolicyTestsAidl",
+ "modules-utils-build",
+ ],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "sts",
+ ],
+ certificate: ":cts-netpolicy-app",
+}
diff --git a/tests/cts/hostside-network-policy/app2/AndroidManifest.xml b/tests/cts/hostside-network-policy/app2/AndroidManifest.xml
new file mode 100644
index 0000000..668f2da
--- /dev/null
+++ b/tests/cts/hostside-network-policy/app2/AndroidManifest.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2024 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.netpolicy.hostside.app2">
+
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
+
+ <!--
+ This application is used to listen to RESTRICT_BACKGROUND_CHANGED intents and store
+ them in a shared preferences which is then read by the test app. These broadcasts are
+ handled by 2 listeners, one defined the manifest and another dynamically registered by
+ a service.
+
+ The manifest-defined listener also handles ordered broadcasts used to share data with the
+ test app.
+
+ This application also provides a service, RemoteSocketFactoryService, that the test app can
+ use to open sockets to remote hosts as a different user ID.
+ -->
+ <application android:usesCleartextTraffic="true"
+ android:testOnly="true"
+ android:debuggable="true">
+
+ <activity android:name=".MyActivity"
+ android:exported="true"/>
+ <service android:name=".MyService"
+ android:exported="true"/>
+ <service android:name=".MyForegroundService"
+ android:foregroundServiceType="specialUse"
+ android:exported="true">
+ <property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
+ android:value="Connectivity" />
+ </service>
+ <receiver android:name=".MyBroadcastReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.net.conn.RESTRICT_BACKGROUND_CHANGED"/>
+ <action android:name="com.android.cts.netpolicy.hostside.app2.action.GET_COUNTERS"/>
+ <action android:name="com.android.cts.netpolicy.hostside.app2.action.GET_RESTRICT_BACKGROUND_STATUS"/>
+ <action android:name="com.android.cts.netpolicy.hostside.app2.action.CHECK_NETWORK"/>
+ <action android:name="com.android.cts.netpolicy.hostside.app2.action.SEND_NOTIFICATION"/>
+ <action android:name="com.android.cts.netpolicy.hostside.app2.action.SHOW_TOAST"/>
+ </intent-filter>
+ </receiver>
+ <service android:name=".MyJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" />
+ </application>
+
+ <!--
+ Adding this to make sure that receiving the broadcast is not restricted by
+ package visibility restrictions.
+ -->
+ <queries>
+ <package android:name="android" />
+ </queries>
+
+</manifest>
diff --git a/tests/cts/hostside/app2/res/drawable/ic_notification.png b/tests/cts/hostside-network-policy/app2/res/drawable/ic_notification.png
similarity index 100%
rename from tests/cts/hostside/app2/res/drawable/ic_notification.png
rename to tests/cts/hostside-network-policy/app2/res/drawable/ic_notification.png
Binary files differ
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java b/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/Common.java
similarity index 91%
rename from tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
rename to tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/Common.java
index 1c45579..1719f9b 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
+++ b/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/Common.java
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.cts.net.hostside.app2;
+package com.android.cts.netpolicy.hostside.app2;
-import static com.android.cts.net.hostside.INetworkStateObserver.RESULT_ERROR_OTHER;
-import static com.android.cts.net.hostside.INetworkStateObserver.RESULT_ERROR_UNEXPECTED_CAPABILITIES;
-import static com.android.cts.net.hostside.INetworkStateObserver.RESULT_ERROR_UNEXPECTED_PROC_STATE;
+import static com.android.cts.netpolicy.hostside.INetworkStateObserver.RESULT_ERROR_OTHER;
+import static com.android.cts.netpolicy.hostside.INetworkStateObserver.RESULT_ERROR_UNEXPECTED_CAPABILITIES;
+import static com.android.cts.netpolicy.hostside.INetworkStateObserver.RESULT_ERROR_UNEXPECTED_PROC_STATE;
import android.app.ActivityManager;
import android.content.Context;
@@ -31,8 +31,8 @@
import android.os.RemoteException;
import android.util.Log;
-import com.android.cts.net.hostside.INetworkStateObserver;
-import com.android.cts.net.hostside.NetworkCheckResult;
+import com.android.cts.netpolicy.hostside.INetworkStateObserver;
+import com.android.cts.netpolicy.hostside.NetworkCheckResult;
import java.net.HttpURLConnection;
import java.net.InetAddress;
@@ -49,13 +49,13 @@
static final String DYNAMIC_RECEIVER = "DynamicReceiver";
static final String ACTION_RECEIVER_READY =
- "com.android.cts.net.hostside.app2.action.RECEIVER_READY";
+ "com.android.cts.netpolicy.hostside.app2.action.RECEIVER_READY";
static final String ACTION_FINISH_ACTIVITY =
- "com.android.cts.net.hostside.app2.action.FINISH_ACTIVITY";
+ "com.android.cts.netpolicy.hostside.app2.action.FINISH_ACTIVITY";
static final String ACTION_FINISH_JOB =
- "com.android.cts.net.hostside.app2.action.FINISH_JOB";
+ "com.android.cts.netpolicy.hostside.app2.action.FINISH_JOB";
static final String ACTION_SHOW_TOAST =
- "com.android.cts.net.hostside.app2.action.SHOW_TOAST";
+ "com.android.cts.netpolicy.hostside.app2.action.SHOW_TOAST";
// Copied from com.android.server.net.NetworkPolicyManagerService class
static final String ACTION_SNOOZE_WARNING =
"com.android.server.net.action.SNOOZE_WARNING";
@@ -71,7 +71,7 @@
static final String NOTIFICATION_TYPE_ACTION_BUNDLE = "ACTION_BUNDLE";
static final String NOTIFICATION_TYPE_ACTION_REMOTE_INPUT = "ACTION_REMOTE_INPUT";
- static final String TEST_PKG = "com.android.cts.net.hostside";
+ static final String TEST_PKG = "com.android.cts.netpolicy.hostside";
static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
static final String KEY_SKIP_VALIDATION_CHECKS = TEST_PKG + ".skip_validation_checks";
static final String KEY_CUSTOM_URL = TEST_PKG + ".custom_url";
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java b/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyActivity.java
similarity index 90%
rename from tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
rename to tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyActivity.java
index aa58ff9..d274c50 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
+++ b/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyActivity.java
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.cts.net.hostside.app2;
+package com.android.cts.netpolicy.hostside.app2;
-import static com.android.cts.net.hostside.app2.Common.ACTION_FINISH_ACTIVITY;
-import static com.android.cts.net.hostside.app2.Common.TAG;
-import static com.android.cts.net.hostside.app2.Common.TYPE_COMPONENT_ACTIVTY;
+import static com.android.cts.netpolicy.hostside.app2.Common.ACTION_FINISH_ACTIVITY;
+import static com.android.cts.netpolicy.hostside.app2.Common.TAG;
+import static com.android.cts.netpolicy.hostside.app2.Common.TYPE_COMPONENT_ACTIVTY;
import android.app.Activity;
import android.content.BroadcastReceiver;
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java b/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyBroadcastReceiver.java
similarity index 86%
rename from tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
rename to tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyBroadcastReceiver.java
index 1fd3745..27aec8c 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
+++ b/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyBroadcastReceiver.java
@@ -14,22 +14,22 @@
* limitations under the License.
*/
-package com.android.cts.net.hostside.app2;
+package com.android.cts.netpolicy.hostside.app2;
import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
-import static com.android.cts.net.hostside.app2.Common.ACTION_RECEIVER_READY;
-import static com.android.cts.net.hostside.app2.Common.ACTION_SHOW_TOAST;
-import static com.android.cts.net.hostside.app2.Common.ACTION_SNOOZE_WARNING;
-import static com.android.cts.net.hostside.app2.Common.MANIFEST_RECEIVER;
-import static com.android.cts.net.hostside.app2.Common.NOTIFICATION_TYPE_ACTION;
-import static com.android.cts.net.hostside.app2.Common.NOTIFICATION_TYPE_ACTION_BUNDLE;
-import static com.android.cts.net.hostside.app2.Common.NOTIFICATION_TYPE_ACTION_REMOTE_INPUT;
-import static com.android.cts.net.hostside.app2.Common.NOTIFICATION_TYPE_BUNDLE;
-import static com.android.cts.net.hostside.app2.Common.NOTIFICATION_TYPE_CONTENT;
-import static com.android.cts.net.hostside.app2.Common.NOTIFICATION_TYPE_DELETE;
-import static com.android.cts.net.hostside.app2.Common.NOTIFICATION_TYPE_FULL_SCREEN;
-import static com.android.cts.net.hostside.app2.Common.TAG;
+import static com.android.cts.netpolicy.hostside.app2.Common.ACTION_RECEIVER_READY;
+import static com.android.cts.netpolicy.hostside.app2.Common.ACTION_SHOW_TOAST;
+import static com.android.cts.netpolicy.hostside.app2.Common.ACTION_SNOOZE_WARNING;
+import static com.android.cts.netpolicy.hostside.app2.Common.MANIFEST_RECEIVER;
+import static com.android.cts.netpolicy.hostside.app2.Common.NOTIFICATION_TYPE_ACTION;
+import static com.android.cts.netpolicy.hostside.app2.Common.NOTIFICATION_TYPE_ACTION_BUNDLE;
+import static com.android.cts.netpolicy.hostside.app2.Common.NOTIFICATION_TYPE_ACTION_REMOTE_INPUT;
+import static com.android.cts.netpolicy.hostside.app2.Common.NOTIFICATION_TYPE_BUNDLE;
+import static com.android.cts.netpolicy.hostside.app2.Common.NOTIFICATION_TYPE_CONTENT;
+import static com.android.cts.netpolicy.hostside.app2.Common.NOTIFICATION_TYPE_DELETE;
+import static com.android.cts.netpolicy.hostside.app2.Common.NOTIFICATION_TYPE_FULL_SCREEN;
+import static com.android.cts.netpolicy.hostside.app2.Common.TAG;
import android.app.Notification;
import android.app.Notification.Action;
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java b/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyForegroundService.java
similarity index 87%
rename from tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java
rename to tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyForegroundService.java
index b55761c..54cee3c 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java
+++ b/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyForegroundService.java
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.cts.net.hostside.app2;
+package com.android.cts.netpolicy.hostside.app2;
-import static com.android.cts.net.hostside.app2.Common.TAG;
-import static com.android.cts.net.hostside.app2.Common.TEST_PKG;
-import static com.android.cts.net.hostside.app2.Common.TYPE_COMPONENT_FOREGROUND_SERVICE;
+import static com.android.cts.netpolicy.hostside.app2.Common.TAG;
+import static com.android.cts.netpolicy.hostside.app2.Common.TEST_PKG;
+import static com.android.cts.netpolicy.hostside.app2.Common.TYPE_COMPONENT_FOREGROUND_SERVICE;
import android.R;
import android.app.Notification;
@@ -31,7 +31,7 @@
import android.os.RemoteException;
import android.util.Log;
-import com.android.cts.net.hostside.INetworkStateObserver;
+import com.android.cts.netpolicy.hostside.INetworkStateObserver;
/**
* Service used to change app state to FOREGROUND_SERVICE.
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyJobService.java b/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyJobService.java
similarity index 90%
rename from tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyJobService.java
rename to tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyJobService.java
index 8c112b6..eba55ed 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyJobService.java
+++ b/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyJobService.java
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.cts.net.hostside.app2;
+package com.android.cts.netpolicy.hostside.app2;
-import static com.android.cts.net.hostside.app2.Common.ACTION_FINISH_JOB;
-import static com.android.cts.net.hostside.app2.Common.TAG;
-import static com.android.cts.net.hostside.app2.Common.TYPE_COMPONENT_EXPEDITED_JOB;
+import static com.android.cts.netpolicy.hostside.app2.Common.ACTION_FINISH_JOB;
+import static com.android.cts.netpolicy.hostside.app2.Common.TAG;
+import static com.android.cts.netpolicy.hostside.app2.Common.TYPE_COMPONENT_EXPEDITED_JOB;
import android.app.job.JobParameters;
import android.app.job.JobService;
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java b/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyService.java
similarity index 92%
rename from tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java
rename to tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyService.java
index 5010234..71bcead 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java
+++ b/tests/cts/hostside-network-policy/app2/src/com/android/cts/netpolicy/hostside/app2/MyService.java
@@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.cts.net.hostside.app2;
+package com.android.cts.netpolicy.hostside.app2;
import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
-import static com.android.cts.net.hostside.app2.Common.ACTION_RECEIVER_READY;
-import static com.android.cts.net.hostside.app2.Common.ACTION_SNOOZE_WARNING;
-import static com.android.cts.net.hostside.app2.Common.DYNAMIC_RECEIVER;
-import static com.android.cts.net.hostside.app2.Common.TAG;
+import static com.android.cts.netpolicy.hostside.app2.Common.ACTION_RECEIVER_READY;
+import static com.android.cts.netpolicy.hostside.app2.Common.ACTION_SNOOZE_WARNING;
+import static com.android.cts.netpolicy.hostside.app2.Common.DYNAMIC_RECEIVER;
+import static com.android.cts.netpolicy.hostside.app2.Common.TAG;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@@ -38,9 +38,9 @@
import android.os.RemoteException;
import android.util.Log;
-import com.android.cts.net.hostside.IMyService;
-import com.android.cts.net.hostside.INetworkCallback;
-import com.android.cts.net.hostside.NetworkCheckResult;
+import com.android.cts.netpolicy.hostside.IMyService;
+import com.android.cts.netpolicy.hostside.INetworkCallback;
+import com.android.cts.netpolicy.hostside.NetworkCheckResult;
import com.android.modules.utils.build.SdkLevel;
/**
diff --git a/tests/cts/hostside-network-policy/certs/Android.bp b/tests/cts/hostside-network-policy/certs/Android.bp
new file mode 100644
index 0000000..bfbc341
--- /dev/null
+++ b/tests/cts/hostside-network-policy/certs/Android.bp
@@ -0,0 +1,9 @@
+package {
+ default_team: "trendy_team_framework_backstage_power",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_app_certificate {
+ name: "cts-netpolicy-app",
+ certificate: "cts-net-app",
+}
diff --git a/tests/cts/hostside/certs/README b/tests/cts/hostside-network-policy/certs/README
similarity index 100%
rename from tests/cts/hostside/certs/README
rename to tests/cts/hostside-network-policy/certs/README
diff --git a/tests/cts/hostside/certs/cts-net-app.pk8 b/tests/cts/hostside-network-policy/certs/cts-net-app.pk8
similarity index 100%
rename from tests/cts/hostside/certs/cts-net-app.pk8
rename to tests/cts/hostside-network-policy/certs/cts-net-app.pk8
Binary files differ
diff --git a/tests/cts/hostside/certs/cts-net-app.x509.pem b/tests/cts/hostside-network-policy/certs/cts-net-app.x509.pem
similarity index 100%
rename from tests/cts/hostside/certs/cts-net-app.x509.pem
rename to tests/cts/hostside-network-policy/certs/cts-net-app.x509.pem
diff --git a/tests/cts/hostside/instrumentation_arguments/Android.bp b/tests/cts/hostside-network-policy/instrumentation_arguments/Android.bp
similarity index 100%
rename from tests/cts/hostside/instrumentation_arguments/Android.bp
rename to tests/cts/hostside-network-policy/instrumentation_arguments/Android.bp
diff --git a/tests/cts/hostside/instrumentation_arguments/src/com/android/cts/net/arguments/InstrumentationArguments.java b/tests/cts/hostside-network-policy/instrumentation_arguments/src/com/android/cts/netpolicy/arguments/InstrumentationArguments.java
similarity index 94%
rename from tests/cts/hostside/instrumentation_arguments/src/com/android/cts/net/arguments/InstrumentationArguments.java
rename to tests/cts/hostside-network-policy/instrumentation_arguments/src/com/android/cts/netpolicy/arguments/InstrumentationArguments.java
index 911b129..0fe98e9 100644
--- a/tests/cts/hostside/instrumentation_arguments/src/com/android/cts/net/arguments/InstrumentationArguments.java
+++ b/tests/cts/hostside-network-policy/instrumentation_arguments/src/com/android/cts/netpolicy/arguments/InstrumentationArguments.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.cts.net.arguments;
+package com.android.cts.netpolicy.arguments;
public interface InstrumentationArguments {
String ARG_WAIVE_BIND_PRIORITY = "waive_bind_priority";
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java b/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideConnOnActivityStartTest.java
similarity index 91%
rename from tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java
rename to tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideConnOnActivityStartTest.java
index fff716d..422231d 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java
+++ b/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideConnOnActivityStartTest.java
@@ -14,13 +14,12 @@
* limitations under the License.
*/
-package com.android.cts.net;
+package com.android.cts.netpolicy;
-import static com.android.cts.net.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
+import static com.android.cts.netpolicy.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
import android.platform.test.annotations.FlakyTest;
-import com.android.testutils.SkipPresubmit;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
@@ -30,8 +29,8 @@
import java.util.Map;
-@SkipPresubmit(reason = "Out of SLO flakiness")
-public class HostsideConnOnActivityStartTest extends HostsideNetworkTestCase {
+@FlakyTest(bugId = 288324467)
+public class HostsideConnOnActivityStartTest extends HostsideNetworkPolicyTestCase {
private static final String TEST_CLASS = TEST_PKG + ".ConnOnActivityStartTest";
@BeforeClassWithInfo
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideDefaultNetworkRestrictionsTests.java b/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideDefaultNetworkRestrictionsTests.java
similarity index 92%
rename from tests/cts/hostside/src/com/android/cts/net/HostsideDefaultNetworkRestrictionsTests.java
rename to tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideDefaultNetworkRestrictionsTests.java
index faabbef..62952bb 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideDefaultNetworkRestrictionsTests.java
+++ b/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideDefaultNetworkRestrictionsTests.java
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-package com.android.cts.net;
+package com.android.cts.netpolicy;
-import static com.android.cts.net.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
+import static com.android.cts.netpolicy.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
-import com.android.testutils.SkipPresubmit;
+import android.platform.test.annotations.FlakyTest;
+
import com.android.tradefed.device.DeviceNotAvailableException;
import org.junit.After;
@@ -28,8 +29,8 @@
import java.util.Map;
// TODO(b/321848487): Annotate with @RequiresFlagsEnabled to mirror the device-side tests.
-@SkipPresubmit(reason = "Monitoring for flakiness")
-public class HostsideDefaultNetworkRestrictionsTests extends HostsideNetworkTestCase {
+@FlakyTest(bugId = 288324467)
+public class HostsideDefaultNetworkRestrictionsTests extends HostsideNetworkPolicyTestCase {
private static final String METERED_TEST_CLASS = TEST_PKG + ".DefaultRestrictionsMeteredTest";
private static final String NON_METERED_TEST_CLASS =
TEST_PKG + ".DefaultRestrictionsNonMeteredTest";
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java b/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideNetworkCallbackTests.java
similarity index 88%
rename from tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java
rename to tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideNetworkCallbackTests.java
index c4bcdfd..2c2b118 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java
+++ b/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideNetworkCallbackTests.java
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.cts.net;
+package com.android.cts.netpolicy;
-import static com.android.cts.net.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
+import static com.android.cts.netpolicy.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
-import com.android.testutils.SkipPresubmit;
+import android.platform.test.annotations.FlakyTest;
import org.junit.After;
import org.junit.Before;
@@ -25,8 +25,8 @@
import java.util.Map;
-@SkipPresubmit(reason = "Out of SLO flakiness")
-public class HostsideNetworkCallbackTests extends HostsideNetworkTestCase {
+@FlakyTest(bugId = 288324467)
+public class HostsideNetworkCallbackTests extends HostsideNetworkPolicyTestCase {
@Before
public void setUp() throws Exception {
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkPolicyManagerTests.java b/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideNetworkPolicyManagerTests.java
similarity index 94%
rename from tests/cts/hostside/src/com/android/cts/net/HostsideNetworkPolicyManagerTests.java
rename to tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideNetworkPolicyManagerTests.java
index 4730b14..8ffe360 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkPolicyManagerTests.java
+++ b/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideNetworkPolicyManagerTests.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.cts.net;
+package com.android.cts.netpolicy;
-import static com.android.cts.net.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
+import static com.android.cts.netpolicy.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
import org.junit.After;
import org.junit.Before;
@@ -24,7 +24,7 @@
import java.util.Map;
-public class HostsideNetworkPolicyManagerTests extends HostsideNetworkTestCase {
+public class HostsideNetworkPolicyManagerTests extends HostsideNetworkPolicyTestCase {
@Before
public void setUp() throws Exception {
uninstallPackage(TEST_APP2_PKG, false);
diff --git a/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideNetworkPolicyTestCase.java b/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideNetworkPolicyTestCase.java
new file mode 100644
index 0000000..b662aa1
--- /dev/null
+++ b/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideNetworkPolicyTestCase.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2024 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.cts.netpolicy;
+
+import static com.android.cts.netpolicy.arguments.InstrumentationArguments.ARG_CONNECTION_CHECK_CUSTOM_URL;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import com.android.ddmlib.Log;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.targetprep.BuildError;
+import com.android.tradefed.targetprep.TargetSetupError;
+import com.android.tradefed.targetprep.suite.SuiteApkInstaller;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
+import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
+import com.android.tradefed.util.RunUtil;
+
+import org.junit.runner.RunWith;
+
+import java.util.Map;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+abstract class HostsideNetworkPolicyTestCase extends BaseHostJUnit4Test {
+ protected static final boolean DEBUG = false;
+ protected static final String TAG = "HostsideNetworkPolicyTests";
+ protected static final String TEST_PKG = "com.android.cts.netpolicy.hostside";
+ protected static final String TEST_APK = "CtsHostsideNetworkPolicyTestsApp.apk";
+ protected static final String TEST_APP2_PKG = "com.android.cts.netpolicy.hostside.app2";
+ protected static final String TEST_APP2_APK = "CtsHostsideNetworkPolicyTestsApp2.apk";
+
+ @Option(name = "custom-url", importance = Option.Importance.IF_UNSET,
+ description = "A custom url to use for testing network connections")
+ protected String mCustomUrl;
+
+ @BeforeClassWithInfo
+ public static void setUpOnceBase(TestInformation testInfo) throws Exception {
+ uninstallPackage(testInfo, TEST_PKG, false);
+ installPackage(testInfo, TEST_APK);
+ }
+
+ @AfterClassWithInfo
+ public static void tearDownOnceBase(TestInformation testInfo)
+ throws DeviceNotAvailableException {
+ uninstallPackage(testInfo, TEST_PKG, true);
+ }
+
+ // Custom static method to install the specified package, this is used to bypass auto-cleanup
+ // per test in BaseHostJUnit4.
+ protected static void installPackage(TestInformation testInfo, String apk)
+ throws DeviceNotAvailableException, TargetSetupError {
+ assertNotNull(testInfo);
+ final int userId = testInfo.getDevice().getCurrentUser();
+ final SuiteApkInstaller installer = new SuiteApkInstaller();
+ // Force the apk clean up
+ installer.setCleanApk(true);
+ installer.addTestFileName(apk);
+ installer.setUserId(userId);
+ installer.setShouldGrantPermission(true);
+ installer.addInstallArg("-t");
+ try {
+ installer.setUp(testInfo);
+ } catch (BuildError e) {
+ throw new TargetSetupError(
+ e.getMessage(), e, testInfo.getDevice().getDeviceDescriptor(), e.getErrorId());
+ }
+ }
+
+ protected void installPackage(String apk) throws DeviceNotAvailableException, TargetSetupError {
+ installPackage(getTestInformation(), apk);
+ }
+
+ protected static void uninstallPackage(TestInformation testInfo, String packageName,
+ boolean shouldSucceed)
+ throws DeviceNotAvailableException {
+ assertNotNull(testInfo);
+ final String result = testInfo.getDevice().uninstallPackage(packageName);
+ if (shouldSucceed) {
+ assertNull("uninstallPackage(" + packageName + ") failed: " + result, result);
+ }
+ }
+
+ protected void uninstallPackage(String packageName,
+ boolean shouldSucceed)
+ throws DeviceNotAvailableException {
+ uninstallPackage(getTestInformation(), packageName, shouldSucceed);
+ }
+
+ protected void assertPackageUninstalled(String packageName) throws DeviceNotAvailableException,
+ InterruptedException {
+ final String command = "cmd package list packages " + packageName;
+ final int max_tries = 5;
+ for (int i = 1; i <= max_tries; i++) {
+ final String result = runCommand(command);
+ if (result.trim().isEmpty()) {
+ return;
+ }
+ // 'list packages' filters by substring, so we need to iterate with the results
+ // and check one by one, otherwise 'com.android.cts.netpolicy.hostside' could return
+ // 'com.android.cts.netpolicy.hostside.app2'
+ boolean found = false;
+ for (String line : result.split("[\\r\\n]+")) {
+ if (line.endsWith(packageName)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return;
+ }
+ i++;
+ Log.v(TAG, "Package " + packageName + " not uninstalled yet (" + result
+ + "); sleeping 1s before polling again");
+ RunUtil.getDefault().sleep(1000);
+ }
+ fail("Package '" + packageName + "' not uinstalled after " + max_tries + " seconds");
+ }
+
+ protected int getUid(String packageName) throws DeviceNotAvailableException {
+ final int currentUser = getDevice().getCurrentUser();
+ final String uidLines = runCommand(
+ "cmd package list packages -U --user " + currentUser + " " + packageName);
+ for (String uidLine : uidLines.split("\n")) {
+ if (uidLine.startsWith("package:" + packageName + " uid:")) {
+ final String[] uidLineParts = uidLine.split(":");
+ // 3rd entry is package uid
+ return Integer.parseInt(uidLineParts[2].trim());
+ }
+ }
+ throw new IllegalStateException("Failed to find the test app on the device; pkg="
+ + packageName + ", u=" + currentUser);
+ }
+
+ protected boolean runDeviceTestsWithCustomOptions(String packageName, String className)
+ throws DeviceNotAvailableException {
+ return runDeviceTestsWithCustomOptions(packageName, className, null);
+ }
+
+ protected boolean runDeviceTestsWithCustomOptions(String packageName, String className,
+ String methodName) throws DeviceNotAvailableException {
+ return runDeviceTestsWithCustomOptions(packageName, className, methodName, null);
+ }
+
+ protected boolean runDeviceTestsWithCustomOptions(String packageName, String className,
+ String methodName, Map<String, String> testArgs) throws DeviceNotAvailableException {
+ final DeviceTestRunOptions deviceTestRunOptions = new DeviceTestRunOptions(packageName)
+ .setTestClassName(className)
+ .setTestMethodName(methodName);
+
+ // Currently there is only one custom option that the test exposes.
+ if (mCustomUrl != null) {
+ deviceTestRunOptions.addInstrumentationArg(ARG_CONNECTION_CHECK_CUSTOM_URL, mCustomUrl);
+ }
+ // Pass over any test specific arguments.
+ if (testArgs != null) {
+ for (Map.Entry<String, String> arg : testArgs.entrySet()) {
+ deviceTestRunOptions.addInstrumentationArg(arg.getKey(), arg.getValue());
+ }
+ }
+ return runDeviceTests(deviceTestRunOptions);
+ }
+
+ protected String runCommand(String command) throws DeviceNotAvailableException {
+ Log.d(TAG, "Command: '" + command + "'");
+ final String output = getDevice().executeShellCommand(command);
+ if (DEBUG) Log.v(TAG, "Output: " + output.trim());
+ return output;
+ }
+}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java b/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideRestrictBackgroundNetworkTests.java
similarity index 98%
rename from tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
rename to tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideRestrictBackgroundNetworkTests.java
index 7b9d3b5..0261c7d 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
+++ b/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/HostsideRestrictBackgroundNetworkTests.java
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.cts.net;
+package com.android.cts.netpolicy;
import static org.junit.Assert.fail;
+import android.platform.test.annotations.FlakyTest;
import android.platform.test.annotations.SecurityTest;
import com.android.ddmlib.Log;
-import com.android.testutils.SkipPresubmit;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.util.RunUtil;
@@ -29,8 +29,8 @@
import org.junit.Before;
import org.junit.Test;
-@SkipPresubmit(reason = "Out of SLO flakiness")
-public class HostsideRestrictBackgroundNetworkTests extends HostsideNetworkTestCase {
+@FlakyTest(bugId = 288324467)
+public class HostsideRestrictBackgroundNetworkTests extends HostsideNetworkPolicyTestCase {
@Before
public void setUp() throws Exception {
diff --git a/tests/cts/hostside/src/com/android/cts/net/NetworkPolicyTestsPreparer.java b/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/NetworkPolicyTestsPreparer.java
similarity index 98%
rename from tests/cts/hostside/src/com/android/cts/net/NetworkPolicyTestsPreparer.java
rename to tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/NetworkPolicyTestsPreparer.java
index 23aca24..cbf2f4d 100644
--- a/tests/cts/hostside/src/com/android/cts/net/NetworkPolicyTestsPreparer.java
+++ b/tests/cts/hostside-network-policy/src/com/android/cts/netpolicy/NetworkPolicyTestsPreparer.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.cts.net;
+package com.android.cts.netpolicy;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
diff --git a/tests/cts/hostside/Android.bp b/tests/cts/hostside/Android.bp
index f6c0430..14d5d54 100644
--- a/tests/cts/hostside/Android.bp
+++ b/tests/cts/hostside/Android.bp
@@ -29,7 +29,6 @@
// Only compile source java files in this apk.
srcs: [
"src/**/*.java",
- ":ArgumentConstants",
],
libs: [
"net-tests-utils-host-device-common",
diff --git a/tests/cts/hostside/AndroidTest.xml b/tests/cts/hostside/AndroidTest.xml
index 0ffe81e..ea6b078 100644
--- a/tests/cts/hostside/AndroidTest.xml
+++ b/tests/cts/hostside/AndroidTest.xml
@@ -22,7 +22,6 @@
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
- <target_preparer class="com.android.cts.net.NetworkPolicyTestsPreparer" />
<!-- Enabling change id ALLOW_TEST_API_ACCESS allows that package to access @TestApi methods -->
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
@@ -30,7 +29,6 @@
<option name="teardown-command" value="am compat reset ALLOW_TEST_API_ACCESS com.android.cts.net.hostside.app2" />
<option name="teardown-command" value="cmd power set-mode 0" />
<option name="teardown-command" value="cmd battery reset" />
- <option name="teardown-command" value="cmd netpolicy stop-watching" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
diff --git a/tests/cts/hostside/OWNERS b/tests/cts/hostside/OWNERS
deleted file mode 100644
index 20bc55e..0000000
--- a/tests/cts/hostside/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Bug component: 61373
-# Inherits parent owners
-sudheersai@google.com
-jchalard@google.com
diff --git a/tests/cts/hostside/app/Android.bp b/tests/cts/hostside/app/Android.bp
index cf4afa9..919e025 100644
--- a/tests/cts/hostside/app/Android.bp
+++ b/tests/cts/hostside/app/Android.bp
@@ -38,7 +38,6 @@
],
srcs: [
"src/**/*.java",
- ":ArgumentConstants",
],
// Tag this module as a cts test artifact
test_suites: [
diff --git a/tests/cts/hostside/app/AndroidManifest.xml b/tests/cts/hostside/app/AndroidManifest.xml
index ca3397b..e0f4cdc 100644
--- a/tests/cts/hostside/app/AndroidManifest.xml
+++ b/tests/cts/hostside/app/AndroidManifest.xml
@@ -43,14 +43,6 @@
<action android:name="android.net.VpnService"/>
</intent-filter>
</service>
- <service android:name=".MyNotificationListenerService"
- android:label="MyNotificationListenerService"
- android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
- android:exported="true">
- <intent-filter>
- <action android:name="android.service.notification.NotificationListenerService"/>
- </intent-filter>
- </service>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
index 0f86d78..8e7b3d4 100755
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -257,7 +257,6 @@
@Before
public void setUp() throws Exception {
- assumeTrue(supportedHardware());
mNetwork = null;
mTestContext = getInstrumentation().getContext();
mTargetContext = getInstrumentation().getTargetContext();
@@ -272,6 +271,7 @@
mDevice.waitForIdle();
mCtsNetUtils = new CtsNetUtils(mTestContext);
mPackageManager = mTestContext.getPackageManager();
+ assumeTrue(supportedHardware());
}
@After
diff --git a/tests/cts/hostside/app2/Android.bp b/tests/cts/hostside/app2/Android.bp
index c526172..ad25562 100644
--- a/tests/cts/hostside/app2/Android.bp
+++ b/tests/cts/hostside/app2/Android.bp
@@ -35,5 +35,4 @@
"general-tests",
"sts",
],
- certificate: ":cts-net-app",
}
diff --git a/tests/cts/hostside/app2/AndroidManifest.xml b/tests/cts/hostside/app2/AndroidManifest.xml
index 2c2d957..412b307 100644
--- a/tests/cts/hostside/app2/AndroidManifest.xml
+++ b/tests/cts/hostside/app2/AndroidManifest.xml
@@ -40,33 +40,8 @@
<application android:usesCleartextTraffic="true"
android:testOnly="true"
android:debuggable="true">
-
- <activity android:name=".MyActivity"
- android:exported="true"/>
- <service android:name=".MyService"
- android:exported="true"/>
- <service android:name=".MyForegroundService"
- android:foregroundServiceType="specialUse"
- android:exported="true">
- <property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
- android:value="Connectivity" />
- </service>
<service android:name=".RemoteSocketFactoryService"
android:exported="true"/>
-
- <receiver android:name=".MyBroadcastReceiver"
- android:exported="true">
- <intent-filter>
- <action android:name="android.net.conn.RESTRICT_BACKGROUND_CHANGED"/>
- <action android:name="com.android.cts.net.hostside.app2.action.GET_COUNTERS"/>
- <action android:name="com.android.cts.net.hostside.app2.action.GET_RESTRICT_BACKGROUND_STATUS"/>
- <action android:name="com.android.cts.net.hostside.app2.action.CHECK_NETWORK"/>
- <action android:name="com.android.cts.net.hostside.app2.action.SEND_NOTIFICATION"/>
- <action android:name="com.android.cts.net.hostside.app2.action.SHOW_TOAST"/>
- </intent-filter>
- </receiver>
- <service android:name=".MyJobService"
- android:permission="android.permission.BIND_JOB_SERVICE" />
</application>
<!--
diff --git a/tests/cts/hostside/certs/Android.bp b/tests/cts/hostside/certs/Android.bp
deleted file mode 100644
index 301973e..0000000
--- a/tests/cts/hostside/certs/Android.bp
+++ /dev/null
@@ -1,9 +0,0 @@
-package {
- default_team: "trendy_team_fwk_core_networking",
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-android_app_certificate {
- name: "cts-net-app",
- certificate: "cts-net-app",
-}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
index d7dfa80..69d61b3 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
@@ -16,15 +16,10 @@
package com.android.cts.net;
-import static com.android.cts.net.arguments.InstrumentationArguments.ARG_CONNECTION_CHECK_CUSTOM_URL;
-
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
-import com.android.ddmlib.Log;
import com.android.modules.utils.build.testing.DeviceSdkLevel;
-import com.android.tradefed.config.Option;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.targetprep.BuildError;
@@ -34,27 +29,17 @@
import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
-import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
-import com.android.tradefed.util.RunUtil;
import org.junit.runner.RunWith;
-import java.util.Map;
-
@RunWith(DeviceJUnit4ClassRunner.class)
abstract class HostsideNetworkTestCase extends BaseHostJUnit4Test {
- protected static final boolean DEBUG = false;
- protected static final String TAG = "HostsideNetworkTests";
protected static final String TEST_PKG = "com.android.cts.net.hostside";
protected static final String TEST_APK = "CtsHostsideNetworkTestsApp.apk";
protected static final String TEST_APK_NEXT = "CtsHostsideNetworkTestsAppNext.apk";
protected static final String TEST_APP2_PKG = "com.android.cts.net.hostside.app2";
protected static final String TEST_APP2_APK = "CtsHostsideNetworkTestsApp2.apk";
- @Option(name = "custom-url", importance = Option.Importance.IF_UNSET,
- description = "A custom url to use for testing network connections")
- protected String mCustomUrl;
-
@BeforeClassWithInfo
public static void setUpOnceBase(TestInformation testInfo) throws Exception {
DeviceSdkLevel deviceSdkLevel = new DeviceSdkLevel(testInfo.getDevice());
@@ -110,85 +95,4 @@
throws DeviceNotAvailableException {
uninstallPackage(getTestInformation(), packageName, shouldSucceed);
}
-
- protected void assertPackageUninstalled(String packageName) throws DeviceNotAvailableException,
- InterruptedException {
- final String command = "cmd package list packages " + packageName;
- final int max_tries = 5;
- for (int i = 1; i <= max_tries; i++) {
- final String result = runCommand(command);
- if (result.trim().isEmpty()) {
- return;
- }
- // 'list packages' filters by substring, so we need to iterate with the results
- // and check one by one, otherwise 'com.android.cts.net.hostside' could return
- // 'com.android.cts.net.hostside.app2'
- boolean found = false;
- for (String line : result.split("[\\r\\n]+")) {
- if (line.endsWith(packageName)) {
- found = true;
- break;
- }
- }
- if (!found) {
- return;
- }
- i++;
- Log.v(TAG, "Package " + packageName + " not uninstalled yet (" + result
- + "); sleeping 1s before polling again");
- RunUtil.getDefault().sleep(1000);
- }
- fail("Package '" + packageName + "' not uinstalled after " + max_tries + " seconds");
- }
-
- protected int getUid(String packageName) throws DeviceNotAvailableException {
- final int currentUser = getDevice().getCurrentUser();
- final String uidLines = runCommand(
- "cmd package list packages -U --user " + currentUser + " " + packageName);
- for (String uidLine : uidLines.split("\n")) {
- if (uidLine.startsWith("package:" + packageName + " uid:")) {
- final String[] uidLineParts = uidLine.split(":");
- // 3rd entry is package uid
- return Integer.parseInt(uidLineParts[2].trim());
- }
- }
- throw new IllegalStateException("Failed to find the test app on the device; pkg="
- + packageName + ", u=" + currentUser);
- }
-
- protected boolean runDeviceTestsWithCustomOptions(String packageName, String className)
- throws DeviceNotAvailableException {
- return runDeviceTestsWithCustomOptions(packageName, className, null);
- }
-
- protected boolean runDeviceTestsWithCustomOptions(String packageName, String className,
- String methodName) throws DeviceNotAvailableException {
- return runDeviceTestsWithCustomOptions(packageName, className, methodName, null);
- }
-
- protected boolean runDeviceTestsWithCustomOptions(String packageName, String className,
- String methodName, Map<String, String> testArgs) throws DeviceNotAvailableException {
- final DeviceTestRunOptions deviceTestRunOptions = new DeviceTestRunOptions(packageName)
- .setTestClassName(className)
- .setTestMethodName(methodName);
-
- // Currently there is only one custom option that the test exposes.
- if (mCustomUrl != null) {
- deviceTestRunOptions.addInstrumentationArg(ARG_CONNECTION_CHECK_CUSTOM_URL, mCustomUrl);
- }
- // Pass over any test specific arguments.
- if (testArgs != null) {
- for (Map.Entry<String, String> arg : testArgs.entrySet()) {
- deviceTestRunOptions.addInstrumentationArg(arg.getKey(), arg.getValue());
- }
- }
- return runDeviceTests(deviceTestRunOptions);
- }
-
- protected String runCommand(String command) throws DeviceNotAvailableException {
- Log.d(TAG, "Command: '" + command + "'");
- final String output = getDevice().executeShellCommand(command);
- if (DEBUG) Log.v(TAG, "Output: " + output.trim());
- return output;
- }
}
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index 074c587..768ba12 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -46,6 +46,7 @@
],
jarjar_rules: "jarjar-rules-shared.txt",
static_libs: [
+ "ApfGeneratorLib",
"bouncycastle-unbundled",
"FrameworksNetCommonTests",
"core-tests-support",
diff --git a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
index 159af00..4621a83 100644
--- a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
+++ b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
@@ -22,36 +22,69 @@
import android.Manifest.permission.WRITE_DEVICE_CONFIG
import android.content.pm.PackageManager.FEATURE_WIFI
import android.net.ConnectivityManager
+import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.net.apf.ApfCapabilities
+import android.net.apf.ApfConstants.ETH_ETHERTYPE_OFFSET
+import android.net.apf.ApfConstants.ICMP6_TYPE_OFFSET
+import android.net.apf.ApfConstants.IPV6_NEXT_HEADER_OFFSET
+import android.net.apf.ApfV4Generator
+import android.net.apf.BaseApfGenerator
+import android.net.apf.BaseApfGenerator.MemorySlot
+import android.net.apf.BaseApfGenerator.Register.R0
+import android.net.apf.BaseApfGenerator.Register.R1
import android.os.Build
+import android.os.Handler
+import android.os.HandlerThread
import android.os.PowerManager
import android.platform.test.annotations.AppModeFull
import android.provider.DeviceConfig
import android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY
+import android.system.Os
import android.system.OsConstants
+import android.system.OsConstants.AF_INET6
+import android.system.OsConstants.ETH_P_IPV6
+import android.system.OsConstants.IPPROTO_ICMPV6
+import android.system.OsConstants.SOCK_DGRAM
+import android.system.OsConstants.SOCK_NONBLOCK
+import android.util.Log
+import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.compatibility.common.util.PropertyUtil.getVsrApiLevel
import com.android.compatibility.common.util.SystemUtil.runShellCommand
import com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow
import com.android.internal.util.HexDump
+import com.android.net.module.util.PacketReader
+import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.NetworkStackModuleTest
+import com.android.testutils.RecorderCallback.CallbackEntry.Available
import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
import com.android.testutils.SkipPresubmit
import com.android.testutils.TestableNetworkCallback
import com.android.testutils.runAsShell
+import com.android.testutils.waitForIdle
+import com.google.common.truth.Expect
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import com.google.common.truth.TruthJUnit.assume
+import java.io.FileDescriptor
import java.lang.Thread
+import java.net.InetSocketAddress
+import java.nio.ByteBuffer
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeoutException
import kotlin.random.Random
+import kotlin.test.assertFailsWith
import kotlin.test.assertNotNull
import org.junit.After
+import org.junit.AfterClass
import org.junit.Before
import org.junit.BeforeClass
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -59,16 +92,57 @@
private const val TIMEOUT_MS = 2000L
private const val APF_NEW_RA_FILTER_VERSION = "apf_new_ra_filter_version"
private const val POLLING_INTERVAL_MS: Int = 100
+private const val RCV_BUFFER_SIZE = 1480
+private const val PING_HEADER_LENGTH = 8
@AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
@RunWith(DevSdkIgnoreRunner::class)
+@RequiresDevice
@NetworkStackModuleTest
+// ByteArray.toHexString is experimental API
+@kotlin.ExperimentalStdlibApi
class ApfIntegrationTest {
companion object {
+ private val PING_DESTINATION = InetSocketAddress("2001:4860:4860::8888", 0)
+
+ private val context = InstrumentationRegistry.getInstrumentation().context
+ 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")
+ val result = pollingCheck({ !powerManager.isInteractive() }, timeout_ms = 2000)
+ assertThat(result).isTrue()
+ }
+
+ fun turnScreenOn() {
+ if (wakeLock.isHeld()) wakeLock.release()
+ runShellCommandOrThrow("input keyevent KEYCODE_WAKEUP")
+ val result = pollingCheck({ powerManager.isInteractive() }, timeout_ms = 2000)
+ assertThat(result).isTrue()
+ }
+
@BeforeClass
@JvmStatic
@Suppress("ktlint:standard:no-multi-spaces")
fun setupOnce() {
+ // TODO: assertions thrown in @BeforeClass / @AfterClass are not well supported in the
+ // test infrastructure. Consider saving excepion and throwing it in setUp().
+ // APF must run when the screen is off and the device is not interactive.
+ turnScreenOff()
+ // Wait for APF to become active.
+ Thread.sleep(1000)
// TODO: check that there is no active wifi network. Otherwise, ApfFilter has already been
// created.
// APF adb cmds are only implemented in ApfFilter.java. Enable experiment to prevent
@@ -82,16 +156,96 @@
)
}
}
+
+ @AfterClass
+ @JvmStatic
+ fun tearDownOnce() {
+ turnScreenOn()
+ }
}
- private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
+ class Icmp6PacketReader(
+ handler: Handler,
+ private val network: Network
+ ) : PacketReader(handler, RCV_BUFFER_SIZE) {
+ private var sockFd: FileDescriptor? = null
+ private var futureReply: CompletableFuture<ByteArray>? = null
+
+ override fun createFd(): FileDescriptor {
+ // sockFd is closed by calling super.stop()
+ val sock = Os.socket(AF_INET6, SOCK_DGRAM or SOCK_NONBLOCK, IPPROTO_ICMPV6)
+ // APF runs only on WiFi, so make sure the socket is bound to the right network.
+ network.bindSocket(sock)
+ sockFd = sock
+ return sock
+ }
+
+ override fun handlePacket(recvbuf: ByteArray, length: Int) {
+ // If zero-length or Type is not echo reply: ignore.
+ if (length == 0 || recvbuf[0] != 0x81.toByte()) {
+ return
+ }
+ // Only copy the ping data and complete the future.
+ val result = recvbuf.sliceArray(8..<length)
+ Log.i(TAG, "Received ping reply: ${result.toHexString()}")
+ futureReply!!.complete(recvbuf.sliceArray(8..<length))
+ }
+
+ fun sendPing(data: ByteArray) {
+ require(data.size == 56)
+
+ // rfc4443#section-4.1: Echo Request Message
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | Type | Code | Checksum |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | Identifier | Sequence Number |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | Data ...
+ // +-+-+-+-+-
+ val icmp6Header = byteArrayOf(0x80.toByte(), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
+ val packet = icmp6Header + data
+ Log.i(TAG, "Sent ping: ${packet.toHexString()}")
+ futureReply = CompletableFuture<ByteArray>()
+ Os.sendto(sockFd!!, packet, 0, packet.size, 0, PING_DESTINATION)
+ }
+
+ fun expectPingReply(): ByteArray {
+ return futureReply!!.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
+ }
+
+ fun expectPingDropped() {
+ assertFailsWith(TimeoutException::class) {
+ futureReply!!.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
+ }
+ }
+
+ override fun start(): Boolean {
+ // Ignore the fact start() could return false or throw an exception.
+ handler.post({ super.start() })
+ handler.waitForIdle(TIMEOUT_MS)
+ return true
+ }
+
+ override fun stop() {
+ handler.post({ super.stop() })
+ handler.waitForIdle(TIMEOUT_MS)
+ }
+ }
+
+ @get:Rule val ignoreRule = DevSdkIgnoreRule()
+ @get:Rule val expect = Expect.create()
+
private val cm by lazy { context.getSystemService(ConnectivityManager::class.java)!! }
private val pm by lazy { context.packageManager }
- private val powerManager by lazy { context.getSystemService(PowerManager::class.java)!! }
- private val wakeLock by lazy { powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG) }
+ private lateinit var network: Network
private lateinit var ifname: String
private lateinit var networkCallback: TestableNetworkCallback
private lateinit var caps: ApfCapabilities
+ private val handlerThread = HandlerThread("$TAG handler thread").apply { start() }
+ private val handler = Handler(handlerThread.looper)
+ private lateinit var packetReader: Icmp6PacketReader
fun getApfCapabilities(): ApfCapabilities {
val caps = runShellCommand("cmd network_stack apf $ifname capabilities").trim()
@@ -102,36 +256,9 @@
return ApfCapabilities(version, maxLen, packetFormat)
}
- 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")
- val result = pollingCheck({ !powerManager.isInteractive() }, timeout_ms = 2000)
- assertThat(result).isTrue()
- }
-
- fun turnScreenOn() {
- if (wakeLock.isHeld()) wakeLock.release()
- runShellCommandOrThrow("input keyevent KEYCODE_WAKEUP")
- val result = pollingCheck({ powerManager.isInteractive() }, timeout_ms = 2000)
- assertThat(result).isTrue()
- }
-
@Before
fun setUp() {
assume().that(pm.hasSystemFeature(FEATURE_WIFI)).isTrue()
- // APF must run when the screen is off and the device is not interactive.
- // TODO: consider running some of the tests with screen on (capabilities, read / write).
- turnScreenOff()
networkCallback = TestableNetworkCallback()
cm.requestNetwork(
@@ -141,6 +268,7 @@
.build(),
networkCallback
)
+ network = networkCallback.expect<Available>().network
networkCallback.eventuallyExpect<LinkPropertiesChanged>(TIMEOUT_MS) {
ifname = assertNotNull(it.lp.interfaceName)
true
@@ -150,17 +278,25 @@
// respective VSR releases and all other tests are based on the capabilities indicated.
runShellCommand("cmd network_stack apf $ifname pause")
caps = getApfCapabilities()
+
+ packetReader = Icmp6PacketReader(handler, network)
+ packetReader.start()
}
@After
fun tearDown() {
+ if (::packetReader.isInitialized) {
+ packetReader.stop()
+ }
+ handlerThread.quitSafely()
+ handlerThread.join()
+
if (::ifname.isInitialized) {
runShellCommand("cmd network_stack apf $ifname resume")
}
if (::networkCallback.isInitialized) {
cm.unregisterNetworkCallback(networkCallback)
}
- turnScreenOn()
}
@Test
@@ -198,7 +334,7 @@
}
fun installProgram(bytes: ByteArray) {
- val prog = HexDump.toHexString(bytes, 0 /* offset */, bytes.size, false /* upperCase */)
+ val prog = bytes.toHexString()
val result = runShellCommandOrThrow("cmd network_stack apf $ifname install $prog").trim()
// runShellCommandOrThrow only throws on S+.
assertThat(result).isEqualTo("success")
@@ -231,4 +367,103 @@
assertWithMessage("read/write $i byte prog failed").that(readResult).isEqualTo(program)
}
}
+
+ fun ApfV4Generator.addPassIfNotIcmpv6EchoReply() {
+ // If not IPv6 -> PASS
+ addLoad16(R0, ETH_ETHERTYPE_OFFSET)
+ addJumpIfR0NotEquals(ETH_P_IPV6.toLong(), BaseApfGenerator.PASS_LABEL)
+
+ // If not ICMPv6 -> PASS
+ addLoad8(R0, IPV6_NEXT_HEADER_OFFSET)
+ addJumpIfR0NotEquals(IPPROTO_ICMPV6.toLong(), BaseApfGenerator.PASS_LABEL)
+
+ // If not echo reply -> PASS
+ addLoad8(R0, ICMP6_TYPE_OFFSET)
+ addJumpIfR0NotEquals(0x81, BaseApfGenerator.PASS_LABEL)
+ }
+
+ // APF integration is mostly broken before V
+ @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Test
+ fun testDropPingReply() {
+ assumeApfVersionSupportAtLeast(4)
+
+ // clear any active APF filter
+ var gen = ApfV4Generator(4).addPass()
+ installProgram(gen.generate())
+ readProgram() // wait for install completion
+
+ // Assert that initial ping does not get filtered.
+ val data = ByteArray(56).also { Random.nextBytes(it) }
+ packetReader.sendPing(data)
+ assertThat(packetReader.expectPingReply()).isEqualTo(data)
+
+ // Generate an APF program that drops the next ping
+ gen = ApfV4Generator(4)
+
+ // If not ICMPv6 Echo Reply -> PASS
+ gen.addPassIfNotIcmpv6EchoReply()
+
+ // if not data matches -> PASS
+ gen.addLoadImmediate(R0, ICMP6_TYPE_OFFSET + PING_HEADER_LENGTH)
+ gen.addJumpIfBytesAtR0NotEqual(data, BaseApfGenerator.PASS_LABEL)
+
+ // else DROP
+ gen.addJump(BaseApfGenerator.DROP_LABEL)
+
+ val program = gen.generate()
+ installProgram(program)
+ readProgram() // wait for install completion
+
+ packetReader.sendPing(data)
+ packetReader.expectPingDropped()
+ }
+
+ fun clearApfMemory() = installProgram(ByteArray(caps.maximumApfProgramSize))
+
+ // APF integration is mostly broken before V
+ @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Test
+ fun testPrefilledMemorySlotsV4() {
+ // Test v4 memory slots on both v4 and v6 interpreters.
+ assumeApfVersionSupportAtLeast(4)
+ clearApfMemory()
+ val gen = ApfV4Generator(4)
+
+ // If not ICMPv6 Echo Reply -> PASS
+ gen.addPassIfNotIcmpv6EchoReply()
+
+ // Store all prefilled memory slots in counter region [500, 520)
+ val counterRegion = 500
+ gen.addLoadImmediate(R1, counterRegion)
+ gen.addLoadFromMemory(R0, MemorySlot.PROGRAM_SIZE)
+ gen.addStoreData(R0, 0)
+ gen.addLoadFromMemory(R0, MemorySlot.RAM_LEN)
+ gen.addStoreData(R0, 4)
+ gen.addLoadFromMemory(R0, MemorySlot.IPV4_HEADER_SIZE)
+ gen.addStoreData(R0, 8)
+ gen.addLoadFromMemory(R0, MemorySlot.PACKET_SIZE)
+ gen.addStoreData(R0, 12)
+ gen.addLoadFromMemory(R0, MemorySlot.FILTER_AGE_SECONDS)
+ gen.addStoreData(R0, 16)
+
+ val program = gen.generate()
+ assertThat(program.size).isLessThan(counterRegion)
+ installProgram(program)
+ readProgram() // wait for install completion
+
+ // Trigger the program by sending a ping and waiting on the reply.
+ val data = ByteArray(56).also { Random.nextBytes(it) }
+ packetReader.sendPing(data)
+ packetReader.expectPingReply()
+
+ val readResult = readProgram()
+ val buffer = ByteBuffer.wrap(readResult, counterRegion, 20 /* length */)
+ expect.withMessage("PROGRAM_SIZE").that(buffer.getInt()).isEqualTo(program.size)
+ expect.withMessage("RAM_LEN").that(buffer.getInt()).isEqualTo(caps.maximumApfProgramSize)
+ expect.withMessage("IPV4_HEADER_SIZE").that(buffer.getInt()).isEqualTo(0)
+ // Ping packet (64) + IPv6 header (40) + ethernet header (14)
+ expect.withMessage("PACKET_SIZE").that(buffer.getInt()).isEqualTo(64 + 40 + 14)
+ expect.withMessage("FILTER_AGE_SECONDS").that(buffer.getInt()).isLessThan(5)
+ }
}
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 4d465ba..5ed4696 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -71,7 +71,9 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
@@ -81,7 +83,6 @@
import static android.net.cts.util.CtsNetUtils.HTTP_PORT;
import static android.net.cts.util.CtsNetUtils.NETWORK_CALLBACK_ACTION;
import static android.net.cts.util.CtsNetUtils.TEST_HOST;
-import static android.net.cts.util.CtsNetUtils.TestNetworkCallback;
import static android.net.cts.util.CtsTetheringUtils.TestTetheringEventCallback;
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
import static android.os.Process.INVALID_UID;
@@ -212,6 +213,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -333,8 +335,6 @@
private final ArraySet<Integer> mNetworkTypes = new ArraySet<>();
private UiAutomation mUiAutomation;
private CtsNetUtils mCtsNetUtils;
- // The registered callbacks.
- private List<NetworkCallback> mRegisteredCallbacks = new ArrayList<>();
// Used for cleanup purposes.
private final List<Range<Integer>> mVpnRequiredUidRanges = new ArrayList<>();
@@ -425,15 +425,12 @@
// All tests in this class require a working Internet connection as they start. Make
// sure there is still one as they end that's ready to use for the next test to use.
mTestValidationConfigRule.runAfterNextCleanup(() -> {
- final TestNetworkCallback callback = new TestNetworkCallback();
- registerDefaultNetworkCallback(callback);
- try {
- assertNotNull("Couldn't restore Internet connectivity",
- callback.waitForAvailable());
- } finally {
- // Unregister all registered callbacks.
- unregisterRegisteredCallbacks();
- }
+ // mTestValidationConfigRule has higher order than networkCallbackRule, so
+ // networkCallbackRule is the outer rule and will be cleaned up after this method.
+ final TestableNetworkCallback callback =
+ networkCallbackRule.registerDefaultNetworkCallback();
+ assertNotNull("Couldn't restore Internet connectivity",
+ callback.eventuallyExpect(CallbackEntry.AVAILABLE));
});
}
@@ -993,10 +990,10 @@
// default network.
return new NetworkRequest.Builder()
.clearCapabilities()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ .addCapability(NET_CAPABILITY_TRUSTED)
+ .addCapability(NET_CAPABILITY_NOT_VPN)
+ .addCapability(NET_CAPABILITY_INTERNET)
.build();
}
@@ -1027,10 +1024,10 @@
final String invalidPrivateDnsServer = "invalidhostname.example.com";
final String goodPrivateDnsServer = "dns.google";
mCtsNetUtils.storePrivateDnsSetting();
- final TestableNetworkCallback cb = new TestableNetworkCallback();
final NetworkRequest networkRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_INTERNET).build();
- registerNetworkCallback(networkRequest, cb);
+ final TestableNetworkCallback cb =
+ networkCallbackRule.registerNetworkCallback(networkRequest);
final Network networkForPrivateDns = mCm.getActiveNetwork();
try {
// Verifying the good private DNS sever
@@ -1068,24 +1065,27 @@
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
// We will register for a WIFI network being available or lost.
- final TestNetworkCallback callback = new TestNetworkCallback();
- registerNetworkCallback(makeWifiNetworkRequest(), callback);
+ final TestableNetworkCallback callback = networkCallbackRule.registerNetworkCallback(
+ makeWifiNetworkRequest());
- final TestNetworkCallback defaultTrackingCallback = new TestNetworkCallback();
- registerDefaultNetworkCallback(defaultTrackingCallback);
+ final TestableNetworkCallback defaultTrackingCallback =
+ networkCallbackRule.registerDefaultNetworkCallback();
- final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback();
- final TestNetworkCallback perUidCallback = new TestNetworkCallback();
- final TestNetworkCallback bestMatchingCallback = new TestNetworkCallback();
+ final TestableNetworkCallback systemDefaultCallback = new TestableNetworkCallback();
+ final TestableNetworkCallback perUidCallback = new TestableNetworkCallback();
+ final TestableNetworkCallback bestMatchingCallback = new TestableNetworkCallback();
final Handler h = new Handler(Looper.getMainLooper());
if (TestUtils.shouldTestSApis()) {
assertThrows(SecurityException.class, () ->
- registerSystemDefaultNetworkCallback(systemDefaultCallback, h));
+ networkCallbackRule.registerSystemDefaultNetworkCallback(
+ systemDefaultCallback, h));
runWithShellPermissionIdentity(() -> {
- registerSystemDefaultNetworkCallback(systemDefaultCallback, h);
- registerDefaultNetworkCallbackForUid(Process.myUid(), perUidCallback, h);
+ networkCallbackRule.registerSystemDefaultNetworkCallback(systemDefaultCallback, h);
+ networkCallbackRule.registerDefaultNetworkCallbackForUid(Process.myUid(),
+ perUidCallback, h);
}, NETWORK_SETTINGS);
- registerBestMatchingNetworkCallback(makeDefaultRequest(), bestMatchingCallback, h);
+ networkCallbackRule.registerBestMatchingNetworkCallback(
+ makeDefaultRequest(), bestMatchingCallback, h);
}
Network wifiNetwork = null;
@@ -1094,24 +1094,22 @@
// Now we should expect to get a network callback about availability of the wifi
// network even if it was already connected as a state-based action when the callback
// is registered.
- wifiNetwork = callback.waitForAvailable();
+ wifiNetwork = callback.eventuallyExpect(CallbackEntry.AVAILABLE).getNetwork();
assertNotNull("Did not receive onAvailable for TRANSPORT_WIFI request",
wifiNetwork);
- final Network defaultNetwork = defaultTrackingCallback.waitForAvailable();
+ final Network defaultNetwork = defaultTrackingCallback.eventuallyExpect(
+ CallbackEntry.AVAILABLE).getNetwork();
assertNotNull("Did not receive onAvailable on default network callback",
defaultNetwork);
if (TestUtils.shouldTestSApis()) {
- assertNotNull("Did not receive onAvailable on system default network callback",
- systemDefaultCallback.waitForAvailable());
- final Network perUidNetwork = perUidCallback.waitForAvailable();
- assertNotNull("Did not receive onAvailable on per-UID default network callback",
- perUidNetwork);
+ systemDefaultCallback.eventuallyExpect(CallbackEntry.AVAILABLE);
+ final Network perUidNetwork = perUidCallback.eventuallyExpect(CallbackEntry.AVAILABLE)
+ .getNetwork();
assertEquals(defaultNetwork, perUidNetwork);
- final Network bestMatchingNetwork = bestMatchingCallback.waitForAvailable();
- assertNotNull("Did not receive onAvailable on best matching network callback",
- bestMatchingNetwork);
+ final Network bestMatchingNetwork = bestMatchingCallback.eventuallyExpect(
+ CallbackEntry.AVAILABLE).getNetwork();
assertEquals(defaultNetwork, bestMatchingNetwork);
}
}
@@ -1123,8 +1121,8 @@
final Handler h = new Handler(Looper.getMainLooper());
// Verify registerSystemDefaultNetworkCallback can be accessed via
// CONNECTIVITY_USE_RESTRICTED_NETWORKS permission.
- runWithShellPermissionIdentity(() ->
- registerSystemDefaultNetworkCallback(new TestNetworkCallback(), h),
+ runWithShellPermissionIdentity(
+ () -> networkCallbackRule.registerSystemDefaultNetworkCallback(h),
CONNECTIVITY_USE_RESTRICTED_NETWORKS);
}
@@ -1294,15 +1292,14 @@
*/
@AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
@Test
- public void testRequestNetworkCallback() throws Exception {
- final TestNetworkCallback callback = new TestNetworkCallback();
- requestNetwork(new NetworkRequest.Builder()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .build(), callback);
+ public void testRequestNetworkCallback() {
+ final TestableNetworkCallback callback = networkCallbackRule.requestNetwork(
+ new NetworkRequest.Builder().addCapability(
+ NET_CAPABILITY_INTERNET)
+ .build());
// Wait to get callback for availability of internet
- Network internetNetwork = callback.waitForAvailable();
- assertNotNull("Did not receive NetworkCallback#onAvailable for INTERNET", internetNetwork);
+ callback.eventuallyExpect(CallbackEntry.AVAILABLE).getNetwork();
}
/**
@@ -1320,16 +1317,13 @@
}
}
- final TestNetworkCallback callback = new TestNetworkCallback();
- requestNetwork(new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build(),
- callback, 100);
+ final TestableNetworkCallback callback = networkCallbackRule.requestNetwork(
+ new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build(),
+ 100 /* timeoutMs */);
try {
// Wait to get callback for unavailability of requested network
- assertTrue("Did not receive NetworkCallback#onUnavailable",
- callback.waitForUnavailable());
- } catch (InterruptedException e) {
- fail("NetworkCallback wait was interrupted.");
+ callback.eventuallyExpect(CallbackEntry.UNAVAILABLE, 2_000 /* timeoutMs */);
} finally {
if (previousWifiEnabledState) {
mCtsNetUtils.connectToWifi();
@@ -1416,40 +1410,48 @@
final boolean useSystemDefault)
throws Exception {
final CompletableFuture<Network> networkFuture = new CompletableFuture<>();
- final NetworkCallback networkCallback = new NetworkCallback() {
- @Override
- public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
- if (!nc.hasTransport(targetTransportType)) return;
- final boolean metered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED);
- final boolean validated = nc.hasCapability(NET_CAPABILITY_VALIDATED);
- if (metered == requestedMeteredness && (!waitForValidation || validated)) {
- networkFuture.complete(network);
+ // Registering a callback here guarantees onCapabilitiesChanged is called immediately
+ // with the current setting. Therefore, if the setting has already been changed,
+ // this method will return right away, and if not, it'll wait for the setting to change.
+ final TestableNetworkCallback networkCallback;
+ if (useSystemDefault) {
+ networkCallback = runWithShellPermissionIdentity(() -> {
+ if (isAtLeastS()) {
+ return networkCallbackRule.registerSystemDefaultNetworkCallback(
+ new Handler(Looper.getMainLooper()));
+ } else {
+ // registerSystemDefaultNetworkCallback is only supported on S+.
+ return networkCallbackRule.requestNetwork(
+ new NetworkRequest.Builder()
+ .clearCapabilities()
+ .addCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ .addCapability(NET_CAPABILITY_TRUSTED)
+ .addCapability(NET_CAPABILITY_NOT_VPN)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .build(),
+ new TestableNetworkCallback(),
+ new Handler(Looper.getMainLooper()));
}
- }
- };
-
- try {
- // Registering a callback here guarantees onCapabilitiesChanged is called immediately
- // with the current setting. Therefore, if the setting has already been changed,
- // this method will return right away, and if not, it'll wait for the setting to change.
- if (useSystemDefault) {
- runWithShellPermissionIdentity(() ->
- registerSystemDefaultNetworkCallback(networkCallback,
- new Handler(Looper.getMainLooper())),
- NETWORK_SETTINGS);
- } else {
- registerDefaultNetworkCallback(networkCallback);
- }
-
- // Changing meteredness on wifi involves reconnecting, which can take several seconds
- // (involves re-associating, DHCP...).
- return networkFuture.get(NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
- } catch (TimeoutException e) {
- throw new AssertionError("Timed out waiting for active network metered status to "
- + "change to " + requestedMeteredness + " ; network = "
- + mCm.getActiveNetwork(), e);
+ },
+ NETWORK_SETTINGS);
+ } else {
+ networkCallback = networkCallbackRule.registerDefaultNetworkCallback();
}
+
+ return networkCallback.eventuallyExpect(
+ CallbackEntry.NETWORK_CAPS_UPDATED,
+ // Changing meteredness on wifi involves reconnecting, which can take several
+ // seconds (involves re-associating, DHCP...).
+ NETWORK_CALLBACK_TIMEOUT_MS,
+ cb -> {
+ final NetworkCapabilities nc = cb.getCaps();
+ if (!nc.hasTransport(targetTransportType)) return false;
+
+ final boolean metered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED);
+ final boolean validated = nc.hasCapability(NET_CAPABILITY_VALIDATED);
+ return metered == requestedMeteredness && (!waitForValidation || validated);
+ }).getNetwork();
}
private Network setWifiMeteredStatusAndWait(String ssid, boolean isMetered,
@@ -2091,15 +2093,15 @@
}
private void verifyBindSocketToRestrictedNetworkDisallowed() throws Exception {
- final TestableNetworkCallback testNetworkCb = new TestableNetworkCallback();
final NetworkRequest testRequest = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_TEST)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .removeCapability(NET_CAPABILITY_TRUSTED)
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
.setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
TEST_RESTRICTED_NW_IFACE_NAME))
.build();
- runWithShellPermissionIdentity(() -> requestNetwork(testRequest, testNetworkCb),
+ final TestableNetworkCallback testNetworkCb = runWithShellPermissionIdentity(
+ () -> networkCallbackRule.requestNetwork(testRequest),
CONNECTIVITY_USE_RESTRICTED_NETWORKS,
// CONNECTIVITY_INTERNAL is for requesting restricted network because shell does not
// have CONNECTIVITY_USE_RESTRICTED_NETWORKS on R.
@@ -2115,7 +2117,7 @@
NETWORK_CALLBACK_TIMEOUT_MS,
entry -> network.equals(entry.getNetwork())
&& (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)));
+ .hasCapability(NET_CAPABILITY_NOT_RESTRICTED)));
// CtsNetTestCases package doesn't hold CONNECTIVITY_USE_RESTRICTED_NETWORKS, so it
// does not allow to bind socket to restricted network.
assertThrows(IOException.class, () -> network.bindSocket(socket));
@@ -2238,7 +2240,7 @@
private void registerCallbackAndWaitForAvailable(@NonNull final NetworkRequest request,
@NonNull final TestableNetworkCallback cb) {
- registerNetworkCallback(request, cb);
+ networkCallbackRule.registerNetworkCallback(request, cb);
waitForAvailable(cb);
}
@@ -2346,21 +2348,13 @@
private void verifySsidFromCallbackNetworkCapabilities(@NonNull String ssid, boolean hasSsid)
throws Exception {
- final CompletableFuture<NetworkCapabilities> foundNc = new CompletableFuture();
- final NetworkCallback callback = new NetworkCallback() {
- @Override
- public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
- foundNc.complete(nc);
- }
- };
-
- registerNetworkCallback(makeWifiNetworkRequest(), callback);
+ final TestableNetworkCallback callback =
+ networkCallbackRule.registerNetworkCallback(makeWifiNetworkRequest());
// Registering a callback here guarantees onCapabilitiesChanged is called immediately
// because WiFi network should be connected.
- final NetworkCapabilities nc =
- foundNc.get(NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ final NetworkCapabilities nc = callback.eventuallyExpect(
+ CallbackEntry.NETWORK_CAPS_UPDATED, NETWORK_CALLBACK_TIMEOUT_MS).getCaps();
// Verify if ssid is contained in the NetworkCapabilities received from callback.
- assertNotNull("NetworkCapabilities of the network is null", nc);
assertEquals(hasSsid, Pattern.compile(ssid).matcher(nc.toString()).find());
}
@@ -2389,8 +2383,8 @@
final NetworkRequest testRequest = new NetworkRequest.Builder()
.addTransportType(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)
+ .removeCapability(NET_CAPABILITY_NOT_VPN)
+ .removeCapability(NET_CAPABILITY_TRUSTED)
.setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
testNetworkInterface.getInterfaceName()))
.build();
@@ -2399,14 +2393,15 @@
final TestableNetworkCallback callback = new TestableNetworkCallback();
final Handler handler = new Handler(Looper.getMainLooper());
assertThrows(SecurityException.class,
- () -> requestBackgroundNetwork(testRequest, callback, handler));
+ () -> networkCallbackRule.requestBackgroundNetwork(testRequest, callback, handler));
Network testNetwork = null;
try {
// Request background test network via Shell identity which has NETWORK_SETTINGS
// permission granted.
runWithShellPermissionIdentity(
- () -> requestBackgroundNetwork(testRequest, callback, handler),
+ () -> networkCallbackRule.requestBackgroundNetwork(
+ testRequest, callback, handler),
new String[] { android.Manifest.permission.NETWORK_SETTINGS });
// Register the test network agent which has no foreground request associated to it.
@@ -2499,9 +2494,10 @@
final int otherUid = UserHandle.getUid(5, Process.FIRST_APPLICATION_UID);
final Handler handler = new Handler(Looper.getMainLooper());
- registerDefaultNetworkCallback(myUidCallback, handler);
- runWithShellPermissionIdentity(() -> registerDefaultNetworkCallbackForUid(
- otherUid, otherUidCallback, handler), NETWORK_SETTINGS);
+ networkCallbackRule.registerDefaultNetworkCallback(myUidCallback, handler);
+ runWithShellPermissionIdentity(
+ () -> networkCallbackRule.registerDefaultNetworkCallbackForUid(
+ otherUid, otherUidCallback, handler), NETWORK_SETTINGS);
final Network defaultNetwork = myUidCallback.expect(CallbackEntry.AVAILABLE).getNetwork();
final List<DetailedBlockedStatusCallback> allCallbacks =
@@ -2557,14 +2553,14 @@
assertNotNull(info);
assertEquals(DetailedState.CONNECTED, info.getDetailedState());
- final TestableNetworkCallback callback = new TestableNetworkCallback();
+ final TestableNetworkCallback callback;
try {
mCmShim.setLegacyLockdownVpnEnabled(true);
// setLegacyLockdownVpnEnabled is asynchronous and only takes effect when the
// ConnectivityService handler thread processes it. Ensure it has taken effect by doing
// something that blocks until the handler thread is idle.
- registerDefaultNetworkCallback(callback);
+ callback = networkCallbackRule.registerDefaultNetworkCallback();
waitForAvailable(callback);
// Test one of the effects of setLegacyLockdownVpnEnabled: the fact that any NetworkInfo
@@ -2831,9 +2827,9 @@
private void registerTestOemNetworkPreferenceCallbacks(
@NonNull final TestableNetworkCallback defaultCallback,
@NonNull final TestableNetworkCallback systemDefaultCallback) {
- registerDefaultNetworkCallback(defaultCallback);
+ networkCallbackRule.registerDefaultNetworkCallback(defaultCallback);
runWithShellPermissionIdentity(() ->
- registerSystemDefaultNetworkCallback(systemDefaultCallback,
+ networkCallbackRule.registerSystemDefaultNetworkCallback(systemDefaultCallback,
new Handler(Looper.getMainLooper())), NETWORK_SETTINGS);
}
@@ -2949,18 +2945,18 @@
+ " unless device supports WiFi",
mPackageManager.hasSystemFeature(FEATURE_WIFI));
- final TestNetworkCallback cb = new TestNetworkCallback();
try {
// Wait for partial connectivity to be detected on the network
final Network network = preparePartialConnectivity();
- requestNetwork(makeWifiNetworkRequest(), cb);
+ final TestableNetworkCallback cb = networkCallbackRule.requestNetwork(
+ makeWifiNetworkRequest());
runAsShell(NETWORK_SETTINGS, () -> {
// The always bit is verified in NetworkAgentTest
mCm.setAcceptPartialConnectivity(network, false /* accept */, false /* always */);
});
// Reject partial connectivity network should cause the network being torn down
- assertEquals(network, cb.waitForLost());
+ assertEquals(network, cb.eventuallyExpect(CallbackEntry.LOST).getNetwork());
} finally {
mHttpServer.stop();
// Wifi will not automatically reconnect to the network. ensureWifiDisconnected cannot
@@ -2988,7 +2984,6 @@
assumeTrue("testAcceptPartialConnectivity_validatedNetwork cannot execute"
+ " unless device supports WiFi and telephony", canRunTest);
- final TestableNetworkCallback wifiCb = new TestableNetworkCallback();
try {
// Ensure at least one default network candidate connected.
networkCallbackRule.requestCell();
@@ -2998,7 +2993,8 @@
// guarantee that it won't become the default in the future.
assertNotEquals(wifiNetwork, mCm.getActiveNetwork());
- registerNetworkCallback(makeWifiNetworkRequest(), wifiCb);
+ final TestableNetworkCallback wifiCb = networkCallbackRule.registerNetworkCallback(
+ makeWifiNetworkRequest());
runAsShell(NETWORK_SETTINGS, () -> {
mCm.setAcceptUnvalidated(wifiNetwork, false /* accept */, false /* always */);
});
@@ -3025,8 +3021,6 @@
assumeTrue("testSetAvoidUnvalidated cannot execute"
+ " unless device supports WiFi and telephony", canRunTest);
- final TestableNetworkCallback wifiCb = new TestableNetworkCallback();
- final TestableNetworkCallback defaultCb = new TestableNetworkCallback();
final int previousAvoidBadWifi =
ConnectivitySettingsManager.getNetworkAvoidBadWifi(mContext);
@@ -3036,8 +3030,10 @@
final Network cellNetwork = networkCallbackRule.requestCell();
final Network wifiNetwork = prepareValidatedNetwork();
- registerDefaultNetworkCallback(defaultCb);
- registerNetworkCallback(makeWifiNetworkRequest(), wifiCb);
+ final TestableNetworkCallback defaultCb =
+ networkCallbackRule.registerDefaultNetworkCallback();
+ final TestableNetworkCallback wifiCb = networkCallbackRule.registerNetworkCallback(
+ makeWifiNetworkRequest());
// Verify wifi is the default network.
defaultCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
@@ -3100,20 +3096,11 @@
});
}
- private Network expectNetworkHasCapability(Network network, int expectedNetCap, long timeout)
- throws Exception {
- final CompletableFuture<Network> future = new CompletableFuture();
- final NetworkCallback cb = new NetworkCallback() {
- @Override
- public void onCapabilitiesChanged(Network n, NetworkCapabilities nc) {
- if (n.equals(network) && nc.hasCapability(expectedNetCap)) {
- future.complete(network);
- }
- }
- };
-
- registerNetworkCallback(new NetworkRequest.Builder().build(), cb);
- return future.get(timeout, TimeUnit.MILLISECONDS);
+ private Network expectNetworkHasCapability(Network network, int expectedNetCap, long timeout) {
+ return networkCallbackRule.registerNetworkCallback(new NetworkRequest.Builder().build())
+ .eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED, timeout,
+ cb -> cb.getNetwork().equals(network)
+ && cb.getCaps().hasCapability(expectedNetCap)).getNetwork();
}
private void prepareHttpServer() throws Exception {
@@ -3265,12 +3252,13 @@
// For testing mobile data preferred uids feature, it needs both wifi and cell network.
final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
final Network cellNetwork = networkCallbackRule.requestCell();
- final TestableNetworkCallback defaultTrackingCb = new TestableNetworkCallback();
- final TestableNetworkCallback systemDefaultCb = new TestableNetworkCallback();
final Handler h = new Handler(Looper.getMainLooper());
- runWithShellPermissionIdentity(() -> registerSystemDefaultNetworkCallback(
- systemDefaultCb, h), NETWORK_SETTINGS);
- registerDefaultNetworkCallback(defaultTrackingCb);
+ final TestableNetworkCallback systemDefaultCb = runWithShellPermissionIdentity(
+ () -> networkCallbackRule.registerSystemDefaultNetworkCallback(h),
+ NETWORK_SETTINGS);
+
+ final TestableNetworkCallback defaultTrackingCb =
+ networkCallbackRule.registerDefaultNetworkCallback();
try {
// CtsNetTestCases uid is not listed in MOBILE_DATA_PREFERRED_UIDS setting, so the
@@ -3339,7 +3327,7 @@
// Create test network agent with restricted network.
final NetworkCapabilities nc = new NetworkCapabilities.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_TEST)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
.setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
TEST_RESTRICTED_NW_IFACE_NAME))
.build();
@@ -3373,23 +3361,23 @@
mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
// File a restricted network request with permission first to hold the connection.
- final TestableNetworkCallback testNetworkCb = new TestableNetworkCallback();
final NetworkRequest testRequest = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_TEST)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .removeCapability(NET_CAPABILITY_TRUSTED)
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
.setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
TEST_RESTRICTED_NW_IFACE_NAME))
.build();
- runWithShellPermissionIdentity(() -> requestNetwork(testRequest, testNetworkCb),
+ final TestableNetworkCallback testNetworkCb = runWithShellPermissionIdentity(
+ () -> networkCallbackRule.requestNetwork(testRequest),
CONNECTIVITY_USE_RESTRICTED_NETWORKS);
// File another restricted network request without permission.
final TestableNetworkCallback restrictedNetworkCb = new TestableNetworkCallback();
final NetworkRequest restrictedRequest = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_TEST)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .removeCapability(NET_CAPABILITY_TRUSTED)
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
.setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
TEST_RESTRICTED_NW_IFACE_NAME))
.build();
@@ -3406,7 +3394,7 @@
NETWORK_CALLBACK_TIMEOUT_MS,
entry -> network.equals(entry.getNetwork())
&& (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)));
+ .hasCapability(NET_CAPABILITY_NOT_RESTRICTED)));
// CtsNetTestCases package doesn't hold CONNECTIVITY_USE_RESTRICTED_NETWORKS, so it
// does not allow to bind socket to restricted network.
assertThrows(IOException.class, () -> network.bindSocket(socket));
@@ -3424,13 +3412,13 @@
if (TestUtils.shouldTestTApis()) {
// Uid is in allowed list. Try file network request again.
- requestNetwork(restrictedRequest, restrictedNetworkCb);
+ networkCallbackRule.requestNetwork(restrictedRequest, restrictedNetworkCb);
// Verify that the network is restricted.
restrictedNetworkCb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
NETWORK_CALLBACK_TIMEOUT_MS,
entry -> network.equals(entry.getNetwork())
&& (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
- .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)));
+ .hasCapability(NET_CAPABILITY_NOT_RESTRICTED)));
}
} finally {
agent.unregister();
@@ -3569,6 +3557,8 @@
doTestFirewallBlocking(FIREWALL_CHAIN_DOZABLE, ALLOWLIST);
}
+ // Disable test - needs to be fixed
+ @Ignore
@Test @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @ConnectivityModuleTest
@AppModeFull(reason = "Socket cannot bind in instant app mode")
public void testFirewallBlockingBackground() {
@@ -3728,58 +3718,4 @@
// shims, and @IgnoreUpTo does not check that.
assumeTrue(TestUtils.shouldTestSApis());
}
-
- private void unregisterRegisteredCallbacks() {
- for (NetworkCallback callback: mRegisteredCallbacks) {
- mCm.unregisterNetworkCallback(callback);
- }
- }
-
- private void registerDefaultNetworkCallback(NetworkCallback callback) {
- mCm.registerDefaultNetworkCallback(callback);
- mRegisteredCallbacks.add(callback);
- }
-
- private void registerDefaultNetworkCallback(NetworkCallback callback, Handler handler) {
- mCm.registerDefaultNetworkCallback(callback, handler);
- mRegisteredCallbacks.add(callback);
- }
-
- private void registerNetworkCallback(NetworkRequest request, NetworkCallback callback) {
- mCm.registerNetworkCallback(request, callback);
- mRegisteredCallbacks.add(callback);
- }
-
- private void registerSystemDefaultNetworkCallback(NetworkCallback callback, Handler handler) {
- mCmShim.registerSystemDefaultNetworkCallback(callback, handler);
- mRegisteredCallbacks.add(callback);
- }
-
- private void registerDefaultNetworkCallbackForUid(int uid, NetworkCallback callback,
- Handler handler) throws Exception {
- mCmShim.registerDefaultNetworkCallbackForUid(uid, callback, handler);
- mRegisteredCallbacks.add(callback);
- }
-
- private void requestNetwork(NetworkRequest request, NetworkCallback callback) {
- mCm.requestNetwork(request, callback);
- mRegisteredCallbacks.add(callback);
- }
-
- private void requestNetwork(NetworkRequest request, NetworkCallback callback, int timeoutSec) {
- mCm.requestNetwork(request, callback, timeoutSec);
- mRegisteredCallbacks.add(callback);
- }
-
- private void registerBestMatchingNetworkCallback(NetworkRequest request,
- NetworkCallback callback, Handler handler) {
- mCm.registerBestMatchingNetworkCallback(request, callback, handler);
- mRegisteredCallbacks.add(callback);
- }
-
- private void requestBackgroundNetwork(NetworkRequest request, NetworkCallback callback,
- Handler handler) throws Exception {
- mCmShim.requestBackgroundNetwork(request, callback, handler);
- mRegisteredCallbacks.add(callback);
- }
}
diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
index d052551..6fa2812 100644
--- a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
@@ -905,13 +905,17 @@
val iface = createInterface()
val listener = EthernetStateListener()
addInterfaceStateListener(listener)
+ // Uses eventuallyExpect to account for interfaces that could already exist on device
+ listener.eventuallyExpect(iface, STATE_LINK_UP, ROLE_CLIENT)
+
+ disableInterface(iface).expectResult(iface.name)
+ listener.eventuallyExpect(iface, STATE_LINK_DOWN, ROLE_CLIENT)
+
+ enableInterface(iface).expectResult(iface.name)
listener.expectCallback(iface, STATE_LINK_UP, ROLE_CLIENT)
disableInterface(iface).expectResult(iface.name)
listener.expectCallback(iface, STATE_LINK_DOWN, ROLE_CLIENT)
-
- enableInterface(iface).expectResult(iface.name)
- listener.expectCallback(iface, STATE_LINK_UP, ROLE_CLIENT)
}
@Test
diff --git a/tests/cts/net/src/android/net/cts/IpSecManagerTest.java b/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
index a40ed0f..b5f43d3 100644
--- a/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
@@ -69,9 +69,11 @@
import android.net.IpSecManager.SecurityParameterIndex;
import android.net.IpSecManager.UdpEncapsulationSocket;
import android.net.IpSecTransform;
+import android.net.IpSecTransformState;
import android.net.NetworkUtils;
import android.net.TrafficStats;
import android.os.Build;
+import android.os.OutcomeReceiver;
import android.platform.test.annotations.AppModeFull;
import android.system.ErrnoException;
import android.system.Os;
@@ -81,6 +83,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.modules.utils.build.SdkLevel;
+import com.android.testutils.ConnectivityModuleTest;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.SkipMainlinePresubmit;
@@ -100,7 +103,11 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+@ConnectivityModuleTest
@RunWith(AndroidJUnit4.class)
@AppModeFull(reason = "Socket cannot bind in instant app mode")
public class IpSecManagerTest extends IpSecBaseTest {
@@ -444,6 +451,11 @@
long uidTxDelta = 0;
long uidRxDelta = 0;
for (int i = 0; i < 100; i++) {
+ // Clear TrafficStats cache is needed to avoid rate-limit caching for
+ // TrafficStats API results on V+ devices.
+ if (SdkLevel.isAtLeastV()) {
+ runAsShell(NETWORK_SETTINGS, () -> TrafficStats.clearRateLimitCaches());
+ }
uidTxDelta = TrafficStats.getUidTxPackets(Os.getuid()) - uidTxPackets;
uidRxDelta = TrafficStats.getUidRxPackets(Os.getuid()) - uidRxPackets;
@@ -518,6 +530,11 @@
}
private static void initStatsChecker() throws Exception {
+ // Clear TrafficStats cache is needed to avoid rate-limit caching for
+ // TrafficStats API results on V+ devices.
+ if (SdkLevel.isAtLeastV()) {
+ runAsShell(NETWORK_SETTINGS, () -> TrafficStats.clearRateLimitCaches());
+ }
uidTxBytes = TrafficStats.getUidTxBytes(Os.getuid());
uidRxBytes = TrafficStats.getUidRxBytes(Os.getuid());
uidTxPackets = TrafficStats.getUidTxPackets(Os.getuid());
@@ -1642,4 +1659,37 @@
newReplayBitmap(expectedPacketCount));
}
}
+
+ @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Test
+ public void testRequestIpSecTransformStateOnClosedTransform() throws Exception {
+ assumeRequestIpSecTransformStateSupported();
+
+ final InetAddress localAddr = InetAddresses.parseNumericAddress(IPV6_LOOPBACK);
+ final CompletableFuture<RuntimeException> futureError = new CompletableFuture<>();
+
+ try (SecurityParameterIndex spi = mISM.allocateSecurityParameterIndex(localAddr);
+ IpSecTransform transform =
+ buildTransportModeTransform(spi, localAddr, null /* encapSocket*/)) {
+ transform.close();
+
+ transform.requestIpSecTransformState(
+ Executors.newSingleThreadExecutor(),
+ new OutcomeReceiver<IpSecTransformState, RuntimeException>() {
+ @Override
+ public void onResult(IpSecTransformState state) {
+ fail("Expect to fail but received a state");
+ }
+
+ @Override
+ public void onError(RuntimeException error) {
+ futureError.complete(error);
+ }
+ });
+
+ assertTrue(
+ futureError.get(SOCK_TIMEOUT, TimeUnit.MILLISECONDS)
+ instanceof IllegalStateException);
+ }
+ }
}
diff --git a/tests/cts/net/src/android/net/cts/MdnsTestUtils.kt b/tests/cts/net/src/android/net/cts/MdnsTestUtils.kt
index 5ba6c4c..93cec9c 100644
--- a/tests/cts/net/src/android/net/cts/MdnsTestUtils.kt
+++ b/tests/cts/net/src/android/net/cts/MdnsTestUtils.kt
@@ -287,6 +287,12 @@
): TestDnsPacket? = pollForMdnsPacket(timeoutMs) { it.isQueryFor(recordName, *requiredTypes) }
fun TapPacketReader.pollForReply(
+ recordName: String,
+ type: Int,
+ timeoutMs: Long = MDNS_REGISTRATION_TIMEOUT_MS
+): TestDnsPacket? = pollForMdnsPacket(timeoutMs) { it.isReplyFor(recordName, type) }
+
+fun TapPacketReader.pollForReply(
serviceName: String,
serviceType: String,
timeoutMs: Long = MDNS_REGISTRATION_TIMEOUT_MS
diff --git a/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java b/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
index 73f65e0..06a827b 100644
--- a/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
+++ b/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
@@ -265,6 +265,14 @@
* Get all testable Networks with internet capability.
*/
private Set<Network> getTestableNetworks() throws InterruptedException {
+ // Calling requestNetwork() to request a cell or Wi-Fi network via CtsNetUtils or
+ // NetworkCallbackRule requires the CHANGE_NETWORK_STATE permission. This permission cannot
+ // be granted to instant apps. Therefore, return currently available testable networks
+ // directly in instant mode.
+ if (mContext.getApplicationInfo().isInstantApp()) {
+ return new ArraySet<>(mCtsNetUtils.getTestableNetworks());
+ }
+
// Obtain cell and Wi-Fi through CtsNetUtils (which uses NetworkCallbacks), as they may have
// just been reconnected by the test using NetworkCallbacks, so synchronous calls may not
// yet return them (synchronous calls and callbacks should not be mixed for a given
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index 6dd4857..6394599 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -81,7 +81,9 @@
import com.android.compatibility.common.util.SystemUtil
import com.android.modules.utils.build.SdkLevel.isAtLeastU
import com.android.net.module.util.DnsPacket
+import com.android.net.module.util.DnsPacket.ANSECTION
import com.android.net.module.util.HexDump
+import com.android.net.module.util.HexDump.hexStringToByteArray
import com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_LEN
import com.android.net.module.util.PacketBuilder
import com.android.testutils.ConnectivityModuleTest
@@ -96,6 +98,7 @@
import com.android.testutils.TestableNetworkAgent
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkCreated
import com.android.testutils.TestableNetworkCallback
+import com.android.testutils.assertContainsExactly
import com.android.testutils.assertEmpty
import com.android.testutils.filters.CtsNetTestCasesMaxTargetSdk30
import com.android.testutils.filters.CtsNetTestCasesMaxTargetSdk33
@@ -127,6 +130,7 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import kotlin.test.assertNotEquals
private const val TAG = "NsdManagerTest"
private const val TIMEOUT_MS = 2000L
@@ -138,6 +142,9 @@
private const val DBG = false
private const val TEST_PORT = 12345
private const val MDNS_PORT = 5353.toShort()
+private const val TYPE_KEY = 25
+private const val QCLASS_INTERNET = 0x0001
+private const val NAME_RECORDS_TTL_MILLIS: Long = 120
private val multicastIpv6Addr = parseNumericAddress("ff02::fb") as Inet6Address
private val testSrcAddr = parseNumericAddress("2001:db8::123") as Inet6Address
@@ -167,6 +174,12 @@
private val serviceType2 = "_nmt%09d._tcp".format(Random().nextInt(1_000_000_000))
private val customHostname = "NsdTestHost%09d".format(Random().nextInt(1_000_000_000))
private val customHostname2 = "NsdTestHost%09d".format(Random().nextInt(1_000_000_000))
+ private val publicKey = hexStringToByteArray(
+ "0201030dc141d0637960b98cbc12cfca"
+ + "221d2879dac26ee5b460e9007c992e19"
+ + "02d897c391b03764d448f7d0c772fdb0"
+ + "3b1d9d6d52ff8886769e8e2362513565"
+ + "270962d3")
private val handlerThread = HandlerThread(NsdManagerTest::class.java.simpleName)
private val ctsNetUtils by lazy{ CtsNetUtils(context) }
@@ -1451,10 +1464,8 @@
handlerThread.waitForIdle(TIMEOUT_MS)
tryTest {
- repeat(3) {
- assertNotNull(packetReader.pollForAdvertisement(serviceName, serviceType),
- "Expect 3 announcements sent after initial probing")
- }
+ assertNotNull(packetReader.pollForAdvertisement(serviceName, serviceType),
+ "No announcements sent after initial probing")
assertEquals(si.serviceName, registeredService.serviceName)
assertEquals(si.hostname, registeredService.hostname)
@@ -2027,7 +2038,7 @@
}
@Test
- fun testAdvertisingAndDiscovery_multipleRegistrationsForSameCustomHost_unionOfAddressesFound() {
+ fun testAdvertisingAndDiscovery_multipleRegistrationsForSameCustomHost_hostRenamed() {
val hostAddresses1 = listOf(
parseNumericAddress("192.0.2.23"),
parseNumericAddress("2001:db8::1"),
@@ -2035,9 +2046,6 @@
val hostAddresses2 = listOf(
parseNumericAddress("192.0.2.24"),
parseNumericAddress("2001:db8::3"))
- val hostAddresses3 = listOf(
- parseNumericAddress("2001:db8::3"),
- parseNumericAddress("2001:db8::5"))
val si1 = NsdServiceInfo().also {
it.network = testNetwork1.network
it.hostname = customHostname
@@ -2051,18 +2059,9 @@
it.hostname = customHostname
it.hostAddresses = hostAddresses2
}
- val si3 = NsdServiceInfo().also {
- it.network = testNetwork1.network
- it.serviceName = serviceName3
- it.serviceType = serviceType
- it.port = TEST_PORT + 1
- it.hostname = customHostname
- it.hostAddresses = hostAddresses3
- }
val registrationRecord1 = NsdRegistrationRecord()
val registrationRecord2 = NsdRegistrationRecord()
- val registrationRecord3 = NsdRegistrationRecord()
val discoveryRecord = NsdDiscoveryRecord()
tryTest {
@@ -2072,27 +2071,13 @@
nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
testNetwork1.network, Executor { it.run() }, discoveryRecord)
- val discoveredInfo1 = discoveryRecord.waitForServiceDiscovered(
+ val discoveredInfo = discoveryRecord.waitForServiceDiscovered(
serviceName, serviceType, testNetwork1.network)
- val resolvedInfo1 = resolveService(discoveredInfo1)
+ val resolvedInfo = resolveService(discoveredInfo)
- assertEquals(TEST_PORT, resolvedInfo1.port)
- assertEquals(si1.hostname, resolvedInfo1.hostname)
- assertAddressEquals(
- hostAddresses1 + hostAddresses2,
- resolvedInfo1.hostAddresses)
-
- registerService(registrationRecord3, si3)
-
- val discoveredInfo2 = discoveryRecord.waitForServiceDiscovered(
- serviceName3, serviceType, testNetwork1.network)
- val resolvedInfo2 = resolveService(discoveredInfo2)
-
- assertEquals(TEST_PORT + 1, resolvedInfo2.port)
- assertEquals(si2.hostname, resolvedInfo2.hostname)
- assertAddressEquals(
- hostAddresses1 + hostAddresses2 + hostAddresses3,
- resolvedInfo2.hostAddresses)
+ assertEquals(TEST_PORT, resolvedInfo.port)
+ assertNotEquals(si1.hostname, resolvedInfo.hostname)
+ assertAddressEquals(hostAddresses2, resolvedInfo.hostAddresses)
} cleanupStep {
nsdManager.stopServiceDiscovery(discoveryRecord)
@@ -2100,7 +2085,6 @@
} cleanup {
nsdManager.unregisterService(registrationRecord1)
nsdManager.unregisterService(registrationRecord2)
- nsdManager.unregisterService(registrationRecord3)
}
}
@@ -2266,6 +2250,165 @@
}
@Test
+ fun testAdvertising_registerServiceAndPublicKey_keyAnnounced() {
+ val si = NsdServiceInfo().also {
+ it.network = testNetwork1.network
+ it.serviceType = serviceType
+ it.serviceName = serviceName
+ it.port = TEST_PORT
+ it.publicKey = publicKey
+ }
+ val packetReader = TapPacketReader(Handler(handlerThread.looper),
+ testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */)
+ packetReader.startAsyncForTest()
+ handlerThread.waitForIdle(TIMEOUT_MS)
+
+ val registrationRecord = NsdRegistrationRecord()
+ val discoveryRecord = NsdDiscoveryRecord()
+ tryTest {
+ registerService(registrationRecord, si)
+
+ val announcement = packetReader.pollForReply(
+ "$serviceName.$serviceType.local",
+ TYPE_KEY
+ )
+ assertNotNull(announcement)
+ val keyRecords = announcement.records[ANSECTION].filter { it.nsType == TYPE_KEY }
+ assertEquals(1, keyRecords.size)
+ val actualRecord = keyRecords.get(0)
+ assertEquals(TYPE_KEY, actualRecord.nsType)
+ assertEquals("$serviceName.$serviceType.local", actualRecord.dName)
+ assertEquals(NAME_RECORDS_TTL_MILLIS, actualRecord.ttl)
+ assertArrayEquals(publicKey, actualRecord.rr)
+
+ nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD,
+ testNetwork1.network, Executor { it.run() }, discoveryRecord)
+
+ val discoveredInfo1 = discoveryRecord.waitForServiceDiscovered(
+ serviceName, serviceType, testNetwork1.network)
+ val resolvedInfo1 = resolveService(discoveredInfo1)
+
+ assertEquals(serviceName, discoveredInfo1.serviceName)
+ assertEquals(TEST_PORT, resolvedInfo1.port)
+ } cleanupStep {
+ nsdManager.stopServiceDiscovery(discoveryRecord)
+
+ discoveryRecord.expectCallback<DiscoveryStopped>()
+ } cleanup {
+ nsdManager.unregisterService(registrationRecord)
+ }
+ }
+
+ @Test
+ fun testAdvertising_registerCustomHostAndPublicKey_keyAnnounced() {
+ val si = NsdServiceInfo().also {
+ it.network = testNetwork1.network
+ it.hostname = customHostname
+ it.hostAddresses = listOf(
+ parseNumericAddress("192.0.2.23"),
+ parseNumericAddress("2001:db8::1"),
+ parseNumericAddress("2001:db8::2"))
+ it.publicKey = publicKey
+ }
+ val packetReader = TapPacketReader(Handler(handlerThread.looper),
+ testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */)
+ packetReader.startAsyncForTest()
+ handlerThread.waitForIdle(TIMEOUT_MS)
+
+ val registrationRecord = NsdRegistrationRecord()
+ tryTest {
+ registerService(registrationRecord, si)
+
+ val announcement = packetReader.pollForReply("$customHostname.local", TYPE_KEY)
+ assertNotNull(announcement)
+ val keyRecords = announcement.records[ANSECTION].filter { it.nsType == TYPE_KEY }
+ assertEquals(1, keyRecords.size)
+ val actualRecord = keyRecords.get(0)
+ assertEquals(TYPE_KEY, actualRecord.nsType)
+ assertEquals("$customHostname.local", actualRecord.dName)
+ assertEquals(NAME_RECORDS_TTL_MILLIS, actualRecord.ttl)
+ assertArrayEquals(publicKey, actualRecord.rr)
+
+ // This test case focuses on key announcement so we don't check the details of the
+ // announcement of the custom host addresses.
+ val addressRecords = announcement.records[ANSECTION].filter {
+ it.nsType == DnsResolver.TYPE_AAAA ||
+ it.nsType == DnsResolver.TYPE_A
+ }
+ assertEquals(3, addressRecords.size)
+ } cleanup {
+ nsdManager.unregisterService(registrationRecord)
+ }
+ }
+
+ @Test
+ fun testAdvertising_registerTwoServicesWithSameCustomHostAndPublicKey_keyAnnounced() {
+ val si1 = NsdServiceInfo().also {
+ it.network = testNetwork1.network
+ it.serviceType = serviceType
+ it.serviceName = serviceName
+ it.port = TEST_PORT
+ it.hostname = customHostname
+ it.hostAddresses = listOf(
+ parseNumericAddress("192.0.2.23"),
+ parseNumericAddress("2001:db8::1"),
+ parseNumericAddress("2001:db8::2"))
+ it.publicKey = publicKey
+ }
+ val si2 = NsdServiceInfo().also {
+ it.network = testNetwork1.network
+ it.serviceType = serviceType2
+ it.serviceName = serviceName2
+ it.port = TEST_PORT + 1
+ it.hostname = customHostname
+ it.hostAddresses = listOf()
+ it.publicKey = publicKey
+ }
+ val packetReader = TapPacketReader(Handler(handlerThread.looper),
+ testNetwork1.iface.fileDescriptor.fileDescriptor, 1500 /* maxPacketSize */)
+ packetReader.startAsyncForTest()
+ handlerThread.waitForIdle(TIMEOUT_MS)
+
+ val registrationRecord1 = NsdRegistrationRecord()
+ val registrationRecord2 = NsdRegistrationRecord()
+ tryTest {
+ registerService(registrationRecord1, si1)
+
+ var announcement =
+ packetReader.pollForReply("$serviceName.$serviceType.local", TYPE_KEY)
+ assertNotNull(announcement)
+ var keyRecords = announcement.records[ANSECTION].filter { it.nsType == TYPE_KEY }
+ assertEquals(2, keyRecords.size)
+ assertTrue(keyRecords.any { it.dName == "$serviceName.$serviceType.local" })
+ assertTrue(keyRecords.any { it.dName == "$customHostname.local" })
+ assertTrue(keyRecords.all { it.ttl == NAME_RECORDS_TTL_MILLIS })
+ assertTrue(keyRecords.all { it.rr.contentEquals(publicKey) })
+
+ // This test case focuses on key announcement so we don't check the details of the
+ // announcement of the custom host addresses.
+ val addressRecords = announcement.records[ANSECTION].filter {
+ it.nsType == DnsResolver.TYPE_AAAA ||
+ it.nsType == DnsResolver.TYPE_A
+ }
+ assertEquals(3, addressRecords.size)
+
+ registerService(registrationRecord2, si2)
+
+ announcement = packetReader.pollForReply("$serviceName2.$serviceType2.local", TYPE_KEY)
+ assertNotNull(announcement)
+ keyRecords = announcement.records[ANSECTION].filter { it.nsType == TYPE_KEY }
+ assertEquals(2, keyRecords.size)
+ assertTrue(keyRecords.any { it.dName == "$serviceName2.$serviceType2.local" })
+ assertTrue(keyRecords.any { it.dName == "$customHostname.local" })
+ assertTrue(keyRecords.all { it.ttl == NAME_RECORDS_TTL_MILLIS })
+ assertTrue(keyRecords.all { it.rr.contentEquals(publicKey) })
+ } cleanup {
+ nsdManager.unregisterService(registrationRecord1)
+ nsdManager.unregisterService(registrationRecord2)
+ }
+ }
+
+ @Test
fun testServiceTypeClientRemovedAfterSocketDestroyed() {
val si = makeTestServiceInfo(testNetwork1.network)
// Register service on testNetwork1
diff --git a/tests/integration/src/com/android/server/net/integrationtests/NetworkStatsIntegrationTest.kt b/tests/integration/src/com/android/server/net/integrationtests/NetworkStatsIntegrationTest.kt
index 52e502d..4780c5d 100644
--- a/tests/integration/src/com/android/server/net/integrationtests/NetworkStatsIntegrationTest.kt
+++ b/tests/integration/src/com/android/server/net/integrationtests/NetworkStatsIntegrationTest.kt
@@ -38,6 +38,7 @@
import android.os.Build
import android.os.Process
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.modules.utils.build.SdkLevel
import com.android.server.net.integrationtests.NetworkStatsIntegrationTest.Direction.DOWNLOAD
import com.android.server.net.integrationtests.NetworkStatsIntegrationTest.Direction.UPLOAD
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
@@ -214,6 +215,11 @@
// In practice, for one way 10k download payload, the download usage is about
// 11222~12880 bytes, with 14~17 packets. And the upload usage is about 1279~1626 bytes
// with 14~17 packets, which is majorly contributed by TCP ACK packets.
+ // Clear TrafficStats cache is needed to avoid rate-limit caching for
+ // TrafficStats API results on V+ devices.
+ if (SdkLevel.isAtLeastV()) {
+ TrafficStats.clearRateLimitCaches()
+ }
val snapshotAfterDownload = StatsSnapshot(context, internalInterfaceName)
val (expectedDownloadLower, expectedDownloadUpper) = getExpectedStatsBounds(
TEST_DOWNLOAD_SIZE,
@@ -236,6 +242,9 @@
)
// Verify upload data usage accounting.
+ if (SdkLevel.isAtLeastV()) {
+ TrafficStats.clearRateLimitCaches()
+ }
val snapshotAfterUpload = StatsSnapshot(context, internalInterfaceName)
val (expectedUploadLower, expectedUploadUpper) = getExpectedStatsBounds(
TEST_UPLOAD_SIZE,
diff --git a/tests/unit/java/android/net/NetworkStackBpfNetMapsTest.kt b/tests/unit/java/android/net/NetworkStackBpfNetMapsTest.kt
index a9ccbdd..b5d78f3 100644
--- a/tests/unit/java/android/net/NetworkStackBpfNetMapsTest.kt
+++ b/tests/unit/java/android/net/NetworkStackBpfNetMapsTest.kt
@@ -21,7 +21,8 @@
import android.net.BpfNetMapsConstants.DATA_SAVER_ENABLED_KEY
import android.net.BpfNetMapsConstants.DOZABLE_MATCH
import android.net.BpfNetMapsConstants.HAPPY_BOX_MATCH
-import android.net.BpfNetMapsConstants.PENALTY_BOX_MATCH
+import android.net.BpfNetMapsConstants.PENALTY_BOX_ADMIN_MATCH
+import android.net.BpfNetMapsConstants.PENALTY_BOX_USER_MATCH
import android.net.BpfNetMapsConstants.STANDBY_MATCH
import android.net.BpfNetMapsConstants.UID_RULES_CONFIGURATION_KEY
import android.net.BpfNetMapsUtils.getMatchByFirewallChain
@@ -48,9 +49,9 @@
private const val TEST_UID3 = TEST_UID2 + 1
private const val NO_IIF = 0
-// pre-T devices does not support Bpf.
+// NetworkStack can not use this before U due to b/326143935
@RunWith(DevSdkIgnoreRunner::class)
-@IgnoreUpTo(VERSION_CODES.S_V2)
+@IgnoreUpTo(VERSION_CODES.TIRAMISU)
class NetworkStackBpfNetMapsTest {
@Rule
@JvmField
@@ -102,14 +103,18 @@
}
// Verify the size matches, this also verifies no common item in allow and deny chains.
assertEquals(
- BpfNetMapsConstants.ALLOW_CHAINS.size +
- BpfNetMapsConstants.DENY_CHAINS.size,
+ BpfNetMapsConstants.ALLOW_CHAINS.size +
+ BpfNetMapsConstants.DENY_CHAINS.size +
+ BpfNetMapsConstants.METERED_ALLOW_CHAINS.size +
+ BpfNetMapsConstants.METERED_DENY_CHAINS.size,
declaredChains.size
)
declaredChains.forEach {
assertTrue(
- BpfNetMapsConstants.ALLOW_CHAINS.contains(it.get(null)) ||
- BpfNetMapsConstants.DENY_CHAINS.contains(it.get(null))
+ BpfNetMapsConstants.ALLOW_CHAINS.contains(it.get(null)) ||
+ BpfNetMapsConstants.METERED_ALLOW_CHAINS.contains(it.get(null)) ||
+ BpfNetMapsConstants.DENY_CHAINS.contains(it.get(null)) ||
+ BpfNetMapsConstants.METERED_DENY_CHAINS.contains(it.get(null))
)
}
}
@@ -190,7 +195,16 @@
// Add uid1 to penalty box, verify the network is blocked for uid1, while uid2 is not
// affected.
- testUidOwnerMap.updateEntry(S32(TEST_UID1), UidOwnerValue(NO_IIF, PENALTY_BOX_MATCH))
+ testUidOwnerMap.updateEntry(S32(TEST_UID1), UidOwnerValue(NO_IIF, PENALTY_BOX_USER_MATCH))
+ assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true))
+ assertFalse(isUidNetworkingBlocked(TEST_UID2, metered = true))
+ testUidOwnerMap.updateEntry(S32(TEST_UID1), UidOwnerValue(NO_IIF, PENALTY_BOX_ADMIN_MATCH))
+ assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true))
+ assertFalse(isUidNetworkingBlocked(TEST_UID2, metered = true))
+ testUidOwnerMap.updateEntry(
+ S32(TEST_UID1),
+ UidOwnerValue(NO_IIF, PENALTY_BOX_USER_MATCH or PENALTY_BOX_ADMIN_MATCH)
+ )
assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true))
assertFalse(isUidNetworkingBlocked(TEST_UID2, metered = true))
@@ -206,7 +220,14 @@
// priority.
testUidOwnerMap.updateEntry(
S32(TEST_UID1),
- UidOwnerValue(NO_IIF, PENALTY_BOX_MATCH or HAPPY_BOX_MATCH)
+ UidOwnerValue(NO_IIF, PENALTY_BOX_USER_MATCH or HAPPY_BOX_MATCH)
+ )
+ assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true))
+ assertTrue(isUidNetworkingBlocked(TEST_UID2, metered = true))
+ assertFalse(isUidNetworkingBlocked(TEST_UID3, metered = true))
+ testUidOwnerMap.updateEntry(
+ S32(TEST_UID1),
+ UidOwnerValue(NO_IIF, PENALTY_BOX_ADMIN_MATCH or HAPPY_BOX_MATCH)
)
assertTrue(isUidNetworkingBlocked(TEST_UID1, metered = true))
assertTrue(isUidNetworkingBlocked(TEST_UID2, metered = true))
@@ -240,7 +261,7 @@
for (uid in FIRST_APPLICATION_UID - 5..FIRST_APPLICATION_UID + 5) {
// system uid is not blocked regardless of firewall chains
val expectBlocked = uid >= FIRST_APPLICATION_UID
- testUidOwnerMap.updateEntry(S32(uid), UidOwnerValue(NO_IIF, PENALTY_BOX_MATCH))
+ testUidOwnerMap.updateEntry(S32(uid), UidOwnerValue(NO_IIF, PENALTY_BOX_USER_MATCH))
assertEquals(
expectBlocked,
isUidNetworkingBlocked(uid, metered = true),
diff --git a/tests/unit/java/android/net/nsd/NsdManagerTest.java b/tests/unit/java/android/net/nsd/NsdManagerTest.java
index 27c4561..9c812a1 100644
--- a/tests/unit/java/android/net/nsd/NsdManagerTest.java
+++ b/tests/unit/java/android/net/nsd/NsdManagerTest.java
@@ -16,6 +16,11 @@
package android.net.nsd;
+import static android.net.InetAddresses.parseNumericAddress;
+import static android.net.nsd.NsdManager.checkServiceInfoForRegistration;
+
+import static com.android.net.module.util.HexDump.hexStringToByteArray;
+
import static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
@@ -54,6 +59,7 @@
import org.mockito.MockitoAnnotations;
import java.net.InetAddress;
+import java.util.Collections;
import java.util.List;
import java.time.Duration;
@@ -395,6 +401,7 @@
NsdManager.RegistrationListener listener4 = mock(NsdManager.RegistrationListener.class);
NsdManager.RegistrationListener listener5 = mock(NsdManager.RegistrationListener.class);
NsdManager.RegistrationListener listener6 = mock(NsdManager.RegistrationListener.class);
+ NsdManager.RegistrationListener listener7 = mock(NsdManager.RegistrationListener.class);
NsdServiceInfo invalidService = new NsdServiceInfo(null, null);
NsdServiceInfo validService = new NsdServiceInfo("a_name", "_a_type._tcp");
@@ -439,6 +446,19 @@
validServiceWithCustomHostNoAddresses.setPort(2222);
validServiceWithCustomHostNoAddresses.setHostname("a_host");
+ NsdServiceInfo validServiceWithPublicKey = new NsdServiceInfo("a_name", "_a_type._tcp");
+ validServiceWithPublicKey.setPublicKey(
+ hexStringToByteArray(
+ "0201030dc141d0637960b98cbc12cfca"
+ + "221d2879dac26ee5b460e9007c992e19"
+ + "02d897c391b03764d448f7d0c772fdb0"
+ + "3b1d9d6d52ff8886769e8e2362513565"
+ + "270962d3"));
+
+ NsdServiceInfo invalidServiceWithTooShortPublicKey =
+ new NsdServiceInfo("a_name", "_a_type._tcp");
+ invalidServiceWithTooShortPublicKey.setPublicKey(hexStringToByteArray("0201"));
+
// Service registration
// - invalid arguments
mustFail(() -> { manager.unregisterService(null); });
@@ -449,6 +469,8 @@
mustFail(() -> { manager.registerService(validService, PROTOCOL, null); });
mustFail(() -> {
manager.registerService(invalidMissingHostnameWithAddresses, PROTOCOL, listener1); });
+ mustFail(() -> {
+ manager.registerService(invalidServiceWithTooShortPublicKey, PROTOCOL, listener1); });
manager.registerService(validService, PROTOCOL, listener1);
// - update without subtype is not allowed
mustFail(() -> { manager.registerService(validServiceDuplicate, PROTOCOL, listener1); });
@@ -479,6 +501,9 @@
// - registering a service with a custom host with no addresses is valid
manager.registerService(validServiceWithCustomHostNoAddresses, PROTOCOL, listener6);
manager.unregisterService(listener6);
+ // - registering a service with a public key is valid
+ manager.registerService(validServiceWithPublicKey, PROTOCOL, listener7);
+ manager.unregisterService(listener7);
// Discover service
// - invalid arguments
@@ -506,6 +531,229 @@
mustFail(() -> { manager.resolveService(validService, listener3); });
}
+ private static final class NsdServiceInfoBuilder {
+ private static final String SERVICE_NAME = "TestService";
+ private static final String SERVICE_TYPE = "_testservice._tcp";
+ private static final int SERVICE_PORT = 12345;
+ private static final String HOSTNAME = "TestHost";
+ private static final List<InetAddress> HOST_ADDRESSES =
+ List.of(parseNumericAddress("192.168.2.23"), parseNumericAddress("2001:db8::3"));
+ private static final byte[] PUBLIC_KEY =
+ hexStringToByteArray(
+ "0201030dc141d0637960b98cbc12cfca"
+ + "221d2879dac26ee5b460e9007c992e19"
+ + "02d897c391b03764d448f7d0c772fdb0"
+ + "3b1d9d6d52ff8886769e8e2362513565"
+ + "270962d3");
+
+ private final NsdServiceInfo mNsdServiceInfo = new NsdServiceInfo();
+
+ NsdServiceInfo build() {
+ return mNsdServiceInfo;
+ }
+
+ NsdServiceInfoBuilder setNoService() {
+ mNsdServiceInfo.setServiceName(null);
+ mNsdServiceInfo.setServiceType(null);
+ mNsdServiceInfo.setPort(0);
+ return this;
+ }
+
+ NsdServiceInfoBuilder setService() {
+ mNsdServiceInfo.setServiceName(SERVICE_NAME);
+ mNsdServiceInfo.setServiceType(SERVICE_TYPE);
+ mNsdServiceInfo.setPort(SERVICE_PORT);
+ return this;
+ }
+
+ NsdServiceInfoBuilder setZeroPortService() {
+ mNsdServiceInfo.setServiceName(SERVICE_NAME);
+ mNsdServiceInfo.setServiceType(SERVICE_TYPE);
+ mNsdServiceInfo.setPort(0);
+ return this;
+ }
+
+ NsdServiceInfoBuilder setInvalidService() {
+ mNsdServiceInfo.setServiceName(SERVICE_NAME);
+ mNsdServiceInfo.setServiceType(null);
+ mNsdServiceInfo.setPort(SERVICE_PORT);
+ return this;
+ }
+
+ NsdServiceInfoBuilder setDefaultHost() {
+ mNsdServiceInfo.setHostname(null);
+ mNsdServiceInfo.setHostAddresses(Collections.emptyList());
+ return this;
+ }
+
+ NsdServiceInfoBuilder setCustomHost() {
+ mNsdServiceInfo.setHostname(HOSTNAME);
+ mNsdServiceInfo.setHostAddresses(HOST_ADDRESSES);
+ return this;
+ }
+
+ NsdServiceInfoBuilder setCustomHostNoAddress() {
+ mNsdServiceInfo.setHostname(HOSTNAME);
+ mNsdServiceInfo.setHostAddresses(Collections.emptyList());
+ return this;
+ }
+
+ NsdServiceInfoBuilder setHostAddressesNoHostname() {
+ mNsdServiceInfo.setHostname(null);
+ mNsdServiceInfo.setHostAddresses(HOST_ADDRESSES);
+ return this;
+ }
+
+ NsdServiceInfoBuilder setNoPublicKey() {
+ mNsdServiceInfo.setPublicKey(null);
+ return this;
+ }
+
+ NsdServiceInfoBuilder setPublicKey() {
+ mNsdServiceInfo.setPublicKey(PUBLIC_KEY);
+ return this;
+ }
+
+ NsdServiceInfoBuilder setInvalidPublicKey() {
+ mNsdServiceInfo.setPublicKey(new byte[3]);
+ return this;
+ }
+ }
+
+ @Test
+ public void testCheckServiceInfoForRegistration() {
+ // The service is invalid
+ mustFail(() -> checkServiceInfoForRegistration(
+ new NsdServiceInfoBuilder()
+ .setInvalidService()
+ .setCustomHost()
+ .setPublicKey().build()));
+ // Keep compatible with the legacy behavior: It's allowed to set host
+ // addresses for a service registration although the host addresses
+ // won't be registered. To register the addresses for a host, the
+ // hostname must be specified.
+ checkServiceInfoForRegistration(
+ new NsdServiceInfoBuilder()
+ .setService()
+ .setHostAddressesNoHostname()
+ .setPublicKey().build());
+ // The public key is invalid
+ mustFail(() -> checkServiceInfoForRegistration(
+ new NsdServiceInfoBuilder()
+ .setService()
+ .setCustomHost()
+ .setInvalidPublicKey().build()));
+ // Invalid combinations
+ // 1. (service, custom host, key): valid
+ checkServiceInfoForRegistration(
+ new NsdServiceInfoBuilder()
+ .setService()
+ .setCustomHost()
+ .setPublicKey().build());
+ // 2. (service, custom host, no key): valid
+ checkServiceInfoForRegistration(
+ new NsdServiceInfoBuilder()
+ .setService()
+ .setCustomHost()
+ .setNoPublicKey().build());
+ // 3. (service, no-address custom host, key): valid
+ checkServiceInfoForRegistration(
+ new NsdServiceInfoBuilder()
+ .setService()
+ .setCustomHostNoAddress()
+ .setPublicKey().build());
+ // 4. (service, no-address custom host, no key): valid
+ checkServiceInfoForRegistration(
+ new NsdServiceInfoBuilder()
+ .setService()
+ .setCustomHostNoAddress()
+ .setNoPublicKey().build());
+ // 5. (service, default host, key): valid
+ checkServiceInfoForRegistration(
+ new NsdServiceInfoBuilder()
+ .setService()
+ .setDefaultHost()
+ .setPublicKey().build());
+ // 6. (service, default host, no key): valid
+ checkServiceInfoForRegistration(
+ new NsdServiceInfoBuilder()
+ .setService()
+ .setDefaultHost()
+ .setNoPublicKey().build());
+ // 7. (0-port service, custom host, valid key): valid
+ checkServiceInfoForRegistration(
+ new NsdServiceInfoBuilder()
+ .setZeroPortService()
+ .setCustomHost()
+ .setPublicKey().build());
+ // 8. (0-port service, custom host, no key): invalid
+ mustFail(() -> checkServiceInfoForRegistration(
+ new NsdServiceInfoBuilder()
+ .setZeroPortService()
+ .setCustomHost()
+ .setNoPublicKey().build()));
+ // 9. (0-port service, no-address custom host, key): valid
+ checkServiceInfoForRegistration(
+ new NsdServiceInfoBuilder()
+ .setZeroPortService()
+ .setCustomHostNoAddress()
+ .setPublicKey().build());
+ // 10. (0-port service, no-address custom host, no key): invalid
+ mustFail(() -> checkServiceInfoForRegistration(
+ new NsdServiceInfoBuilder()
+ .setZeroPortService()
+ .setCustomHostNoAddress()
+ .setNoPublicKey().build()));
+ // 11. (0-port service, default host, key): valid
+ checkServiceInfoForRegistration(
+ new NsdServiceInfoBuilder()
+ .setZeroPortService()
+ .setDefaultHost()
+ .setPublicKey().build());
+ // 12. (0-port service, default host, no key): invalid
+ mustFail(() -> checkServiceInfoForRegistration(
+ new NsdServiceInfoBuilder()
+ .setZeroPortService()
+ .setDefaultHost()
+ .setNoPublicKey().build()));
+ // 13. (no service, custom host, key): valid
+ checkServiceInfoForRegistration(
+ new NsdServiceInfoBuilder()
+ .setNoService()
+ .setCustomHost()
+ .setPublicKey().build());
+ // 14. (no service, custom host, no key): valid
+ checkServiceInfoForRegistration(
+ new NsdServiceInfoBuilder()
+ .setNoService()
+ .setCustomHost()
+ .setNoPublicKey().build());
+ // 15. (no service, no-address custom host, key): valid
+ checkServiceInfoForRegistration(
+ new NsdServiceInfoBuilder()
+ .setNoService()
+ .setCustomHostNoAddress()
+ .setPublicKey().build());
+ // 16. (no service, no-address custom host, no key): invalid
+ mustFail(() -> checkServiceInfoForRegistration(
+ new NsdServiceInfoBuilder()
+ .setNoService()
+ .setCustomHostNoAddress()
+ .setNoPublicKey().build()));
+ // 17. (no service, default host, key): invalid
+ mustFail(() -> checkServiceInfoForRegistration(
+ new NsdServiceInfoBuilder()
+ .setNoService()
+ .setDefaultHost()
+ .setPublicKey().build()));
+ // 18. (no service, default host, no key): invalid
+ mustFail(() -> checkServiceInfoForRegistration(
+ new NsdServiceInfoBuilder()
+ .setNoService()
+ .setDefaultHost()
+ .setNoPublicKey().build()));
+ }
+
public void mustFail(Runnable fn) {
try {
fn.run();
diff --git a/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt b/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt
index 3f6e88d..aa28e5a 100644
--- a/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt
+++ b/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt
@@ -231,8 +231,10 @@
val clientId = 99
val transactionId = 100
val durationMs = 10L
+ val sentQueryCount = 10
val metrics = NetworkNsdReportedMetrics(clientId, deps)
- metrics.reportServiceResolutionStop(true /* isLegacy */, transactionId, durationMs)
+ metrics.reportServiceResolutionStop(
+ true /* isLegacy */, transactionId, durationMs, sentQueryCount)
val eventCaptor = ArgumentCaptor.forClass(NetworkNsdReported::class.java)
verify(deps).statsWrite(eventCaptor.capture())
@@ -243,6 +245,7 @@
assertEquals(NsdEventType.NET_RESOLVE, it.type)
assertEquals(MdnsQueryResult.MQR_SERVICE_RESOLUTION_STOP, it.queryResult)
assertEquals(durationMs, it.eventDurationMillisec)
+ assertEquals(sentQueryCount, it.sentQueryCount)
}
}
diff --git a/tests/unit/java/com/android/server/BpfNetMapsTest.java b/tests/unit/java/com/android/server/BpfNetMapsTest.java
index ea905d5..fa79795 100644
--- a/tests/unit/java/com/android/server/BpfNetMapsTest.java
+++ b/tests/unit/java/com/android/server/BpfNetMapsTest.java
@@ -31,13 +31,17 @@
import static android.net.BpfNetMapsConstants.OEM_DENY_1_MATCH;
import static android.net.BpfNetMapsConstants.OEM_DENY_2_MATCH;
import static android.net.BpfNetMapsConstants.OEM_DENY_3_MATCH;
-import static android.net.BpfNetMapsConstants.PENALTY_BOX_MATCH;
+import static android.net.BpfNetMapsConstants.PENALTY_BOX_ADMIN_MATCH;
+import static android.net.BpfNetMapsConstants.PENALTY_BOX_USER_MATCH;
import static android.net.BpfNetMapsConstants.POWERSAVE_MATCH;
import static android.net.BpfNetMapsConstants.RESTRICTED_MATCH;
import static android.net.BpfNetMapsConstants.STANDBY_MATCH;
import static android.net.BpfNetMapsConstants.UID_RULES_CONFIGURATION_KEY;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_1;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_2;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_3;
@@ -334,146 +338,6 @@
}
}
- private void doTestRemoveNaughtyApp(final int iif, final long match) throws Exception {
- mUidOwnerMap.updateEntry(new S32(TEST_UID), new UidOwnerValue(iif, match));
-
- mBpfNetMaps.removeNaughtyApp(TEST_UID);
-
- checkUidOwnerValue(TEST_UID, iif, match & ~PENALTY_BOX_MATCH);
- }
-
- @Test
- @IgnoreUpTo(Build.VERSION_CODES.S_V2)
- public void testRemoveNaughtyApp() throws Exception {
- doTestRemoveNaughtyApp(NO_IIF, PENALTY_BOX_MATCH);
-
- // PENALTY_BOX_MATCH with other matches
- doTestRemoveNaughtyApp(NO_IIF, PENALTY_BOX_MATCH | DOZABLE_MATCH | POWERSAVE_MATCH);
-
- // PENALTY_BOX_MATCH with IIF_MATCH
- doTestRemoveNaughtyApp(TEST_IF_INDEX, PENALTY_BOX_MATCH | IIF_MATCH);
-
- // PENALTY_BOX_MATCH is not enabled
- doTestRemoveNaughtyApp(NO_IIF, DOZABLE_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH);
- }
-
- @Test
- @IgnoreUpTo(Build.VERSION_CODES.S_V2)
- public void testRemoveNaughtyAppMissingUid() {
- // UidOwnerMap does not have entry for TEST_UID
- assertThrows(ServiceSpecificException.class,
- () -> mBpfNetMaps.removeNaughtyApp(TEST_UID));
- }
-
- @Test
- @IgnoreAfter(Build.VERSION_CODES.S_V2)
- public void testRemoveNaughtyAppBeforeT() {
- assertThrows(UnsupportedOperationException.class,
- () -> mBpfNetMaps.removeNaughtyApp(TEST_UID));
- }
-
- private void doTestAddNaughtyApp(final int iif, final long match) throws Exception {
- if (match != NO_MATCH) {
- mUidOwnerMap.updateEntry(new S32(TEST_UID), new UidOwnerValue(iif, match));
- }
-
- mBpfNetMaps.addNaughtyApp(TEST_UID);
-
- checkUidOwnerValue(TEST_UID, iif, match | PENALTY_BOX_MATCH);
- }
-
- @Test
- @IgnoreUpTo(Build.VERSION_CODES.S_V2)
- public void testAddNaughtyApp() throws Exception {
- doTestAddNaughtyApp(NO_IIF, NO_MATCH);
-
- // Other matches are enabled
- doTestAddNaughtyApp(NO_IIF, DOZABLE_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH);
-
- // IIF_MATCH is enabled
- doTestAddNaughtyApp(TEST_IF_INDEX, IIF_MATCH);
-
- // PENALTY_BOX_MATCH is already enabled
- doTestAddNaughtyApp(NO_IIF, PENALTY_BOX_MATCH | DOZABLE_MATCH);
- }
-
- @Test
- @IgnoreAfter(Build.VERSION_CODES.S_V2)
- public void testAddNaughtyAppBeforeT() {
- assertThrows(UnsupportedOperationException.class,
- () -> mBpfNetMaps.addNaughtyApp(TEST_UID));
- }
-
- private void doTestRemoveNiceApp(final int iif, final long match) throws Exception {
- mUidOwnerMap.updateEntry(new S32(TEST_UID), new UidOwnerValue(iif, match));
-
- mBpfNetMaps.removeNiceApp(TEST_UID);
-
- checkUidOwnerValue(TEST_UID, iif, match & ~HAPPY_BOX_MATCH);
- }
-
- @Test
- @IgnoreUpTo(Build.VERSION_CODES.S_V2)
- public void testRemoveNiceApp() throws Exception {
- doTestRemoveNiceApp(NO_IIF, HAPPY_BOX_MATCH);
-
- // HAPPY_BOX_MATCH with other matches
- doTestRemoveNiceApp(NO_IIF, HAPPY_BOX_MATCH | DOZABLE_MATCH | POWERSAVE_MATCH);
-
- // HAPPY_BOX_MATCH with IIF_MATCH
- doTestRemoveNiceApp(TEST_IF_INDEX, HAPPY_BOX_MATCH | IIF_MATCH);
-
- // HAPPY_BOX_MATCH is not enabled
- doTestRemoveNiceApp(NO_IIF, DOZABLE_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH);
- }
-
- @Test
- @IgnoreUpTo(Build.VERSION_CODES.S_V2)
- public void testRemoveNiceAppMissingUid() {
- // UidOwnerMap does not have entry for TEST_UID
- assertThrows(ServiceSpecificException.class,
- () -> mBpfNetMaps.removeNiceApp(TEST_UID));
- }
-
- @Test
- @IgnoreAfter(Build.VERSION_CODES.S_V2)
- public void testRemoveNiceAppBeforeT() {
- assertThrows(UnsupportedOperationException.class,
- () -> mBpfNetMaps.removeNiceApp(TEST_UID));
- }
-
- private void doTestAddNiceApp(final int iif, final long match) throws Exception {
- if (match != NO_MATCH) {
- mUidOwnerMap.updateEntry(new S32(TEST_UID), new UidOwnerValue(iif, match));
- }
-
- mBpfNetMaps.addNiceApp(TEST_UID);
-
- checkUidOwnerValue(TEST_UID, iif, match | HAPPY_BOX_MATCH);
- }
-
- @Test
- @IgnoreUpTo(Build.VERSION_CODES.S_V2)
- public void testAddNiceApp() throws Exception {
- doTestAddNiceApp(NO_IIF, NO_MATCH);
-
- // Other matches are enabled
- doTestAddNiceApp(NO_IIF, DOZABLE_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH);
-
- // IIF_MATCH is enabled
- doTestAddNiceApp(TEST_IF_INDEX, IIF_MATCH);
-
- // HAPPY_BOX_MATCH is already enabled
- doTestAddNiceApp(NO_IIF, HAPPY_BOX_MATCH | DOZABLE_MATCH);
- }
-
- @Test
- @IgnoreAfter(Build.VERSION_CODES.S_V2)
- public void testAddNiceAppBeforeT() {
- assertThrows(UnsupportedOperationException.class,
- () -> mBpfNetMaps.addNiceApp(TEST_UID));
- }
-
private void doTestUpdateUidLockdownRule(final int iif, final long match, final boolean add)
throws Exception {
if (match != NO_MATCH) {
@@ -658,6 +522,9 @@
doTestSetUidRule(FIREWALL_CHAIN_OEM_DENY_1);
doTestSetUidRule(FIREWALL_CHAIN_OEM_DENY_2);
doTestSetUidRule(FIREWALL_CHAIN_OEM_DENY_3);
+ doTestSetUidRule(FIREWALL_CHAIN_METERED_ALLOW);
+ doTestSetUidRule(FIREWALL_CHAIN_METERED_DENY_USER);
+ doTestSetUidRule(FIREWALL_CHAIN_METERED_DENY_ADMIN);
}
@Test
@@ -1079,7 +946,7 @@
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
public void testDumpUidOwnerMap() throws Exception {
doTestDumpUidOwnerMap(HAPPY_BOX_MATCH, "HAPPY_BOX_MATCH");
- doTestDumpUidOwnerMap(PENALTY_BOX_MATCH, "PENALTY_BOX_MATCH");
+ doTestDumpUidOwnerMap(PENALTY_BOX_USER_MATCH, "PENALTY_BOX_USER_MATCH");
doTestDumpUidOwnerMap(DOZABLE_MATCH, "DOZABLE_MATCH");
doTestDumpUidOwnerMap(STANDBY_MATCH, "STANDBY_MATCH");
doTestDumpUidOwnerMap(POWERSAVE_MATCH, "POWERSAVE_MATCH");
@@ -1089,6 +956,7 @@
doTestDumpUidOwnerMap(OEM_DENY_1_MATCH, "OEM_DENY_1_MATCH");
doTestDumpUidOwnerMap(OEM_DENY_2_MATCH, "OEM_DENY_2_MATCH");
doTestDumpUidOwnerMap(OEM_DENY_3_MATCH, "OEM_DENY_3_MATCH");
+ doTestDumpUidOwnerMap(PENALTY_BOX_ADMIN_MATCH, "PENALTY_BOX_ADMIN_MATCH");
doTestDumpUidOwnerMap(HAPPY_BOX_MATCH | POWERSAVE_MATCH,
"HAPPY_BOX_MATCH POWERSAVE_MATCH");
@@ -1137,7 +1005,6 @@
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
public void testDumpUidOwnerMapConfig() throws Exception {
doTestDumpOwnerMatchConfig(HAPPY_BOX_MATCH, "HAPPY_BOX_MATCH");
- doTestDumpOwnerMatchConfig(PENALTY_BOX_MATCH, "PENALTY_BOX_MATCH");
doTestDumpOwnerMatchConfig(DOZABLE_MATCH, "DOZABLE_MATCH");
doTestDumpOwnerMatchConfig(STANDBY_MATCH, "STANDBY_MATCH");
doTestDumpOwnerMatchConfig(POWERSAVE_MATCH, "POWERSAVE_MATCH");
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 7822fe0..9f13d79 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -63,6 +63,9 @@
import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_1;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_2;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_OEM_DENY_3;
@@ -457,6 +460,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.lang.reflect.Method;
import java.net.DatagramSocket;
import java.net.Inet4Address;
import java.net.Inet6Address;
@@ -799,8 +803,10 @@
// This relies on all contexts for a given user returning the same UM mock
final DevicePolicyManager dpmMock = createContextAsUser(userHandle, 0 /* flags */)
.getSystemService(DevicePolicyManager.class);
- doReturn(value).when(dpmMock).getDeviceOwner();
- doReturn(value).when(mDevicePolicyManager).getDeviceOwner();
+ ComponentName componentName = value == null
+ ? null : new ComponentName(value, "deviceOwnerClass");
+ doReturn(componentName).when(dpmMock).getDeviceOwnerComponentOnAnyUser();
+ doReturn(componentName).when(mDevicePolicyManager).getDeviceOwnerComponentOnAnyUser();
}
@Override
@@ -1721,8 +1727,6 @@
private void mockUidNetworkingBlocked() {
doAnswer(i -> isUidBlocked(mBlockedReasons, i.getArgument(1))
).when(mNetworkPolicyManager).isUidNetworkingBlocked(anyInt(), anyBoolean());
- doAnswer(i -> isUidBlocked(mBlockedReasons, i.getArgument(1))
- ).when(mBpfNetMaps).isUidNetworkingBlocked(anyInt(), anyBoolean());
}
private boolean isUidBlocked(int blockedReasons, boolean meteredNetwork) {
@@ -10499,6 +10503,9 @@
doTestSetUidFirewallRule(FIREWALL_CHAIN_OEM_DENY_1, FIREWALL_RULE_ALLOW);
doTestSetUidFirewallRule(FIREWALL_CHAIN_OEM_DENY_2, FIREWALL_RULE_ALLOW);
doTestSetUidFirewallRule(FIREWALL_CHAIN_OEM_DENY_3, FIREWALL_RULE_ALLOW);
+ doTestSetUidFirewallRule(FIREWALL_CHAIN_METERED_ALLOW, FIREWALL_RULE_DENY);
+ doTestSetUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_USER, FIREWALL_RULE_ALLOW);
+ doTestSetUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_ADMIN, FIREWALL_RULE_ALLOW);
}
@Test @IgnoreUpTo(SC_V2)
@@ -19190,6 +19197,25 @@
verifyClatdStop(null /* inOrder */, MOBILE_IFNAME);
}
- // Note : adding tests is ConnectivityServiceTest is deprecated, as it is too big for
+ private static final int EXPECTED_TEST_METHOD_COUNT = 332;
+
+ @Test
+ public void testTestMethodCount() {
+ final Class<?> testClass = this.getClass();
+
+ int actualTestMethodCount = 0;
+ for (final Method method : testClass.getDeclaredMethods()) {
+ if (method.isAnnotationPresent(Test.class)) {
+ actualTestMethodCount++;
+ }
+ }
+
+ assertEquals("Adding tests in ConnectivityServiceTest is deprecated, "
+ + "as it is too big for maintenance. Please consider adding new tests "
+ + "in subclasses of CSTest instead.",
+ EXPECTED_TEST_METHOD_COUNT, actualTestMethodCount);
+ }
+
+ // Note : adding tests in ConnectivityServiceTest is deprecated, as it is too big for
// maintenance. Please consider adding new tests in subclasses of CSTest instead.
}
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index d91e29c..aece3f7 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -41,6 +41,7 @@
import static com.android.server.NsdService.DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF;
import static com.android.server.NsdService.MdnsListener;
import static com.android.server.NsdService.NO_TRANSACTION;
+import static com.android.server.NsdService.checkHostname;
import static com.android.server.NsdService.parseTypeAndSubtype;
import static com.android.testutils.ContextUtils.mockService;
@@ -53,6 +54,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
@@ -904,7 +906,7 @@
request.getServiceName().equals(ns.getServiceName())
&& request.getServiceType().equals(ns.getServiceType())));
verify(mMetrics).reportServiceResolutionStop(
- true /* isLegacy */, resolveId, 10L /* durationMs */);
+ true /* isLegacy */, resolveId, 10L /* durationMs */, 0 /* sentQueryCount */);
}
@Test
@@ -978,7 +980,7 @@
request.getServiceName().equals(ns.getServiceName())
&& request.getServiceType().equals(ns.getServiceType())));
verify(mMetrics).reportServiceResolutionStop(
- true /* isLegacy */, getAddrId, 10L /* durationMs */);
+ true /* isLegacy */, getAddrId, 10L /* durationMs */, 0 /* sentQueryCount */);
}
private void verifyUpdatedServiceInfo(NsdServiceInfo info, String serviceName,
@@ -1679,20 +1681,23 @@
// Subtypes are not used for resolution, only for discovery
assertEquals(Collections.emptyList(), optionsCaptor.getValue().getSubtypes());
+ final MdnsListener listener = listenerCaptor.getValue();
+ // Callbacks for query sent.
+ listener.onDiscoveryQuerySent(Collections.emptyList(), 1 /* transactionId */);
+
doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
client.stopServiceResolution(resolveListener);
waitForIdle();
// Verify the listener has been unregistered.
- final MdnsListener listener = listenerCaptor.getValue();
verify(mDiscoveryManager, timeout(TIMEOUT_MS))
.unregisterListener(eq(constructedServiceType), eq(listener));
verify(resolveListener, timeout(TIMEOUT_MS)).onResolutionStopped(argThat(ns ->
request.getServiceName().equals(ns.getServiceName())
&& request.getServiceType().equals(ns.getServiceType())));
verify(mSocketProvider, timeout(CLEANUP_DELAY_MS + TIMEOUT_MS)).requestStopWhenInactive();
- verify(mMetrics).reportServiceResolutionStop(
- false /* isLegacy */, listener.mTransactionId, 10L /* durationMs */);
+ verify(mMetrics).reportServiceResolutionStop(false /* isLegacy */, listener.mTransactionId,
+ 10L /* durationMs */, 1 /* sentQueryCount */);
}
@Test
@@ -1723,6 +1728,36 @@
}
@Test
+ public void TestCheckHostname() {
+ // Valid cases
+ assertTrue(checkHostname(null));
+ assertTrue(checkHostname("a"));
+ assertTrue(checkHostname("1"));
+ assertTrue(checkHostname("a-1234-bbbb-cccc000"));
+ assertTrue(checkHostname("A-1234-BBbb-CCCC000"));
+ assertTrue(checkHostname("1234-bbbb-cccc000"));
+ assertTrue(checkHostname("0123456789abcdef"
+ + "0123456789abcdef"
+ + "0123456789abcdef"
+ + "0123456789abcde" // 63 characters
+ ));
+
+ // Invalid cases
+ assertFalse(checkHostname("?"));
+ assertFalse(checkHostname("/"));
+ assertFalse(checkHostname("a-"));
+ assertFalse(checkHostname("B-"));
+ assertFalse(checkHostname("-A"));
+ assertFalse(checkHostname("-b"));
+ assertFalse(checkHostname("-1-"));
+ assertFalse(checkHostname("0123456789abcdef"
+ + "0123456789abcdef"
+ + "0123456789abcdef"
+ + "0123456789abcdef" // 64 characters
+ ));
+ }
+
+ @Test
@EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
public void testEnablePlatformMdnsBackend() {
final NsdManager client = connectClient(mService);
diff --git a/tests/unit/java/com/android/server/connectivity/MulticastRoutingCoordinatorServiceTest.kt b/tests/unit/java/com/android/server/connectivity/MulticastRoutingCoordinatorServiceTest.kt
index 6c2c256..5c994f5 100644
--- a/tests/unit/java/com/android/server/connectivity/MulticastRoutingCoordinatorServiceTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/MulticastRoutingCoordinatorServiceTest.kt
@@ -402,15 +402,18 @@
mService.getVirtualInterfaceIndex(mIfName1), oifsUpdate)
val mf6cctlDel = createStructMf6cctl(mSourceAddress, mGroupAddressScope5,
mService.getVirtualInterfaceIndex(mIfName1), mEmptyOifs)
+ val ifName1Copy = String(mIfName1.toCharArray())
+ val ifName2Copy = String(mIfName2.toCharArray())
+ val ifName3Copy = String(mIfName3.toCharArray())
verify(mDeps).setsockoptMrt6AddMfc(eq(mFd), eq(mf6cctlAdd))
- applyMulticastForwardNone(mIfName1, mIfName2)
+ applyMulticastForwardNone(ifName1Copy, ifName2Copy)
mLooper.dispatchAll()
verify(mDeps).setsockoptMrt6AddMfc(eq(mFd), eq(mf6cctlUpdate))
- applyMulticastForwardNone(mIfName1, mIfName3)
+ applyMulticastForwardNone(ifName1Copy, ifName3Copy)
mLooper.dispatchAll()
verify(mDeps, timeout(TIMEOUT_MS).times(1)).setsockoptMrt6DelMfc(eq(mFd), eq(mf6cctlDel))
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt
index 271cc65..d735dc6 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt
@@ -21,12 +21,13 @@
import android.net.nsd.NsdServiceInfo
import android.os.Build
import android.os.HandlerThread
+import com.android.net.module.util.HexDump.hexStringToByteArray
import com.android.server.connectivity.mdns.MdnsAnnouncer.AnnouncementInfo
import com.android.server.connectivity.mdns.MdnsInterfaceAdvertiser.CONFLICT_HOST
import com.android.server.connectivity.mdns.MdnsInterfaceAdvertiser.CONFLICT_SERVICE
-import com.android.server.connectivity.mdns.MdnsProber.ProbingInfo
import com.android.server.connectivity.mdns.MdnsRecord.TYPE_A
import com.android.server.connectivity.mdns.MdnsRecord.TYPE_AAAA
+import com.android.server.connectivity.mdns.MdnsRecord.TYPE_KEY
import com.android.server.connectivity.mdns.MdnsRecord.TYPE_PTR
import com.android.server.connectivity.mdns.MdnsRecord.TYPE_SRV
import com.android.server.connectivity.mdns.MdnsRecord.TYPE_TXT
@@ -52,10 +53,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.verify
private const val TEST_SERVICE_ID_1 = 42
private const val TEST_SERVICE_ID_2 = 43
@@ -125,6 +122,20 @@
port = TEST_PORT
}
+private val TEST_PUBLIC_KEY = hexStringToByteArray(
+ "0201030dc141d0637960b98cbc12cfca"
+ + "221d2879dac26ee5b460e9007c992e19"
+ + "02d897c391b03764d448f7d0c772fdb0"
+ + "3b1d9d6d52ff8886769e8e2362513565"
+ + "270962d3")
+
+private val TEST_PUBLIC_KEY_2 = hexStringToByteArray(
+ "0201030dc141d0637960b98cbc12cfca"
+ + "221d2879dac26ee5b460e9007c992e19"
+ + "02d897c391b03764d448f7d0c772fdb0"
+ + "3b1d9d6d52ff8886769e8e2362513565"
+ + "270962d4")
+
@RunWith(DevSdkIgnoreRunner::class)
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
class MdnsRecordRepositoryTest {
@@ -132,10 +143,23 @@
private val deps = object : Dependencies() {
override fun getInterfaceInetAddresses(iface: NetworkInterface) =
Collections.enumeration(TEST_ADDRESSES.map { it.address })
+
+ override fun elapsedRealTime() = now
+
+ fun elapse(duration: Long) {
+ now += duration
+ }
+
+ fun resetElapsedRealTime() {
+ now = 100
+ }
+
+ var now: Long = 100
}
@Before
fun setUp() {
+ deps.resetElapsedRealTime();
thread.start()
}
@@ -573,6 +597,7 @@
TYPE_PTR -> return MdnsPointerRecord(name, false /* isUnicast */)
TYPE_SRV -> return MdnsServiceRecord(name, false /* isUnicast */)
TYPE_TXT -> return MdnsTextRecord(name, false /* isUnicast */)
+ TYPE_KEY -> return MdnsKeyRecord(name, false /* isUnicast */)
TYPE_A, TYPE_AAAA -> return MdnsInetAddressRecord(name, type, false /* isUnicast */)
else -> fail("Unexpected question type: $type")
}
@@ -900,6 +925,159 @@
}
@Test
+ fun testGetReply_keyQuestionForServiceName_returnsKeyRecord() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
+
+ repository.addServiceAndFinishProbing(TEST_SERVICE_ID_1, NsdServiceInfo().apply {
+ serviceType = "_testservice._tcp"
+ serviceName = "MyTestService1"
+ port = TEST_PORT
+ publicKey = TEST_PUBLIC_KEY
+ })
+ repository.addServiceAndFinishProbing(TEST_SERVICE_ID_2, NsdServiceInfo().apply {
+ serviceType = "_testservice._tcp"
+ serviceName = "MyTestService2"
+ port = 0 // No SRV RR
+ publicKey = TEST_PUBLIC_KEY
+ })
+ val src = InetSocketAddress(parseNumericAddress("fe80::123"), 5353)
+ val serviceName1 = arrayOf("MyTestService1", "_testservice", "_tcp", "local")
+ val serviceName2 = arrayOf("MyTestService2", "_testservice", "_tcp", "local")
+
+ val query1 = makeQuery(TYPE_KEY to serviceName1)
+ val reply1 = repository.getReply(query1, src)
+
+ assertNotNull(reply1)
+ assertEquals(listOf(MdnsKeyRecord(serviceName1,
+ 0, false, LONG_TTL, TEST_PUBLIC_KEY)),
+ reply1.answers)
+ assertEquals(listOf(),
+ reply1.additionalAnswers)
+
+ val query2 = makeQuery(TYPE_KEY to serviceName2)
+ val reply2 = repository.getReply(query2, src)
+
+ assertNotNull(reply2)
+ assertEquals(listOf(MdnsKeyRecord(serviceName2,
+ 0, false, LONG_TTL, TEST_PUBLIC_KEY)),
+ reply2.answers)
+ assertEquals(listOf(MdnsNsecRecord(serviceName2,
+ 0L, true, SHORT_TTL,
+ serviceName2 /* nextDomain */,
+ intArrayOf(TYPE_KEY))),
+ reply2.additionalAnswers)
+ }
+
+ @Test
+ fun testGetReply_keyQuestionForHostname_returnsKeyRecord() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
+
+ repository.addServiceAndFinishProbing(TEST_SERVICE_ID_1, NsdServiceInfo().apply {
+ hostname = "MyHost1"
+ hostAddresses = listOf(
+ parseNumericAddress("2001:db8::1"),
+ parseNumericAddress("2001:db8::2"))
+ publicKey = TEST_PUBLIC_KEY
+ })
+ repository.addServiceAndFinishProbing(TEST_SERVICE_ID_2, NsdServiceInfo().apply {
+ hostname = "MyHost2"
+ hostAddresses = listOf() // No address records
+ publicKey = TEST_PUBLIC_KEY
+ })
+ val src = InetSocketAddress(parseNumericAddress("fe80::123"), 5353)
+ val hostname1 = arrayOf("MyHost1", "local")
+ val hostname2 = arrayOf("MyHost2", "local")
+
+ val query1 = makeQuery(TYPE_KEY to hostname1)
+ val reply1 = repository.getReply(query1, src)
+
+ assertNotNull(reply1)
+ assertEquals(listOf(MdnsKeyRecord(hostname1,
+ 0, false, LONG_TTL, TEST_PUBLIC_KEY)),
+ reply1.answers)
+ assertEquals(listOf(),
+ reply1.additionalAnswers)
+
+ val query2 = makeQuery(TYPE_KEY to hostname2)
+ val reply2 = repository.getReply(query2, src)
+
+ assertNotNull(reply2)
+ assertEquals(listOf(MdnsKeyRecord(hostname2,
+ 0, false, LONG_TTL, TEST_PUBLIC_KEY)),
+ reply2.answers)
+ assertEquals(listOf(MdnsNsecRecord(hostname2, 0L, true, SHORT_TTL,
+ hostname2 /* nextDomain */,
+ intArrayOf(TYPE_KEY))),
+ reply2.additionalAnswers)
+ }
+
+ @Test
+ fun testGetReply_keyRecordForHostRemoved_noAnswertoKeyQuestion() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
+
+ repository.addServiceAndFinishProbing(TEST_SERVICE_ID_1, NsdServiceInfo().apply {
+ hostname = "MyHost1"
+ hostAddresses = listOf(
+ parseNumericAddress("2001:db8::1"),
+ parseNumericAddress("2001:db8::2"))
+ publicKey = TEST_PUBLIC_KEY
+ })
+ repository.addServiceAndFinishProbing(TEST_SERVICE_ID_2, NsdServiceInfo().apply {
+ hostname = "MyHost2"
+ hostAddresses = listOf() // No address records
+ publicKey = TEST_PUBLIC_KEY
+ })
+ repository.removeService(TEST_SERVICE_ID_1)
+ repository.removeService(TEST_SERVICE_ID_2)
+ val src = InetSocketAddress(parseNumericAddress("fe80::123"), 5353)
+ val hostname1 = arrayOf("MyHost1", "local")
+ val hostname2 = arrayOf("MyHost2", "local")
+
+ val query1 = makeQuery(TYPE_KEY to hostname1)
+ val reply1 = repository.getReply(query1, src)
+
+ assertNull(reply1)
+
+ val query2 = makeQuery(TYPE_KEY to hostname2)
+ val reply2 = repository.getReply(query2, src)
+
+ assertNull(reply2)
+ }
+
+ @Test
+ fun testGetReply_keyRecordForServiceRemoved_noAnswertoKeyQuestion() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
+
+ repository.addServiceAndFinishProbing(TEST_SERVICE_ID_1, NsdServiceInfo().apply {
+ serviceType = "_testservice._tcp"
+ serviceName = "MyTestService1"
+ port = TEST_PORT
+ publicKey = TEST_PUBLIC_KEY
+ })
+ repository.addServiceAndFinishProbing(TEST_SERVICE_ID_2, NsdServiceInfo().apply {
+ serviceType = "_testservice._tcp"
+ serviceName = "MyTestService2"
+ port = 0 // No SRV RR
+ publicKey = TEST_PUBLIC_KEY
+ })
+ repository.removeService(TEST_SERVICE_ID_1)
+ repository.removeService(TEST_SERVICE_ID_2)
+ val src = InetSocketAddress(parseNumericAddress("fe80::123"), 5353)
+ val serviceName1 = arrayOf("MyTestService1", "_testservice", "_tcp", "local")
+ val serviceName2 = arrayOf("MyTestService2", "_testservice", "_tcp", "local")
+
+ val query1 = makeQuery(TYPE_KEY to serviceName1)
+ val reply1 = repository.getReply(query1, src)
+
+ assertNull(reply1)
+
+ val query2 = makeQuery(TYPE_KEY to serviceName2)
+ val reply2 = repository.getReply(query2, src)
+
+ assertNull(reply2)
+ }
+
+ @Test
fun testGetReply_customHostRemoved_noAnswerToAAAAQuestion() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
repository.initWithService(
@@ -1003,6 +1181,102 @@
}
@Test
+ fun testGetReply_ipv4AndIpv6Queries_ipv4AndIpv6Replies() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
+ repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1, setOf(TEST_SUBTYPE))
+ val query = makeQuery(TYPE_PTR to arrayOf("_testservice", "_tcp", "local"))
+
+ val srcIpv4 = InetSocketAddress(parseNumericAddress("192.0.2.123"), 5353)
+ val replyIpv4 = repository.getReply(query, srcIpv4)
+ val srcIpv6 = InetSocketAddress(parseNumericAddress("2001:db8::123"), 5353)
+ val replyIpv6 = repository.getReply(query, srcIpv6)
+
+ assertNotNull(replyIpv4)
+ assertEquals(MdnsConstants.getMdnsIPv4Address(), replyIpv4.destination.address)
+ assertEquals(MdnsConstants.MDNS_PORT, replyIpv4.destination.port)
+ assertNotNull(replyIpv6)
+ assertEquals(MdnsConstants.getMdnsIPv6Address(), replyIpv6.destination.address)
+ assertEquals(MdnsConstants.MDNS_PORT, replyIpv6.destination.port)
+ }
+
+ @Test
+ fun testGetReply_twoIpv4QueriesInOneSecond_theSecondReplyIsThrottled() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
+ repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1, setOf(TEST_SUBTYPE))
+ val query = makeQuery(TYPE_PTR to arrayOf("_testservice", "_tcp", "local"))
+
+ val srcIpv4 = InetSocketAddress(parseNumericAddress("192.0.2.123"), 5353)
+ val firstReplyIpv4 = repository.getReply(query, srcIpv4)
+ deps.elapse(500L)
+ val secondReply = repository.getReply(query, srcIpv4)
+
+ assertNotNull(firstReplyIpv4)
+ assertEquals(MdnsConstants.getMdnsIPv4Address(), firstReplyIpv4.destination.address)
+ assertEquals(MdnsConstants.MDNS_PORT, firstReplyIpv4.destination.port)
+ assertNull(secondReply)
+ }
+
+
+ @Test
+ fun testGetReply_twoIpv6QueriesInOneSecond_theSecondReplyIsThrottled() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
+ repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1, setOf(TEST_SUBTYPE))
+ val query = makeQuery(TYPE_PTR to arrayOf("_testservice", "_tcp", "local"))
+
+ val srcIpv6 = InetSocketAddress(parseNumericAddress("2001:db8::123"), 5353)
+ val firstReplyIpv6 = repository.getReply(query, srcIpv6)
+ deps.elapse(500L)
+ val secondReply = repository.getReply(query, srcIpv6)
+
+ assertNotNull(firstReplyIpv6)
+ assertEquals(MdnsConstants.getMdnsIPv6Address(), firstReplyIpv6.destination.address)
+ assertEquals(MdnsConstants.MDNS_PORT, firstReplyIpv6.destination.port)
+ assertNull(secondReply)
+ }
+
+ @Test
+ fun testGetReply_twoIpv4QueriesInMoreThanOneSecond_repliesAreNotThrottled() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
+ repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1, setOf(TEST_SUBTYPE))
+ val query = makeQuery(TYPE_PTR to arrayOf("_testservice", "_tcp", "local"))
+
+ val srcIpv4 = InetSocketAddress(parseNumericAddress("192.0.2.123"), 5353)
+ val firstReplyIpv4 = repository.getReply(query, srcIpv4)
+ // The longest possible interval that may make the reply throttled is
+ // 1000 (MIN_MULTICAST_REPLY_INTERVAL_MS) + 120 (delay for shared name) = 1120
+ deps.elapse(1121L)
+ val secondReplyIpv4 = repository.getReply(query, srcIpv4)
+
+ assertNotNull(firstReplyIpv4)
+ assertEquals(MdnsConstants.getMdnsIPv4Address(), firstReplyIpv4.destination.address)
+ assertEquals(MdnsConstants.MDNS_PORT, firstReplyIpv4.destination.port)
+ assertNotNull(secondReplyIpv4)
+ assertEquals(MdnsConstants.getMdnsIPv4Address(), secondReplyIpv4.destination.address)
+ assertEquals(MdnsConstants.MDNS_PORT, secondReplyIpv4.destination.port)
+ }
+
+ @Test
+ fun testGetReply_twoIpv6QueriesInMoreThanOneSecond_repliesAreNotThrottled() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
+ repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1, setOf(TEST_SUBTYPE))
+ val query = makeQuery(TYPE_PTR to arrayOf("_testservice", "_tcp", "local"))
+
+ val srcIpv6 = InetSocketAddress(parseNumericAddress("2001:db8::123"), 5353)
+ val firstReplyIpv6 = repository.getReply(query, srcIpv6)
+ // The longest possible interval that may make the reply throttled is
+ // 1000 (MIN_MULTICAST_REPLY_INTERVAL_MS) + 120 (delay for shared name) = 1120
+ deps.elapse(1121L)
+ val secondReplyIpv6 = repository.getReply(query, srcIpv6)
+
+ assertNotNull(firstReplyIpv6)
+ assertEquals(MdnsConstants.getMdnsIPv6Address(), firstReplyIpv6.destination.address)
+ assertEquals(MdnsConstants.MDNS_PORT, firstReplyIpv6.destination.port)
+ assertNotNull(secondReplyIpv6)
+ assertEquals(MdnsConstants.getMdnsIPv6Address(), secondReplyIpv6.destination.address)
+ assertEquals(MdnsConstants.MDNS_PORT, secondReplyIpv6.destination.port)
+ }
+
+ @Test
fun testGetConflictingServices() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* ttl */)
@@ -1117,8 +1391,8 @@
@Test
fun testGetConflictingServices_customHostsReplyHasFewerAddressesThanUs_noConflict() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
- repository.addService(TEST_CUSTOM_HOST_ID_1, TEST_CUSTOM_HOST_1, null /* ttl */)
- repository.addService(TEST_CUSTOM_HOST_ID_2, TEST_CUSTOM_HOST_2, null /* ttl */)
+ repository.addServiceAndFinishProbing(TEST_CUSTOM_HOST_ID_1, TEST_CUSTOM_HOST_1)
+ repository.addServiceAndFinishProbing(TEST_CUSTOM_HOST_ID_2, TEST_CUSTOM_HOST_2)
val packet = MdnsPacket(
0, /* flags */
@@ -1136,10 +1410,30 @@
}
@Test
- fun testGetConflictingServices_customHostsReplyHasIdenticalHosts_noConflict() {
+ fun testGetConflictingServices_customHostsReplyHasSameNameRecord_conflictDuringProbing() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
repository.addService(TEST_CUSTOM_HOST_ID_1, TEST_CUSTOM_HOST_1, null /* ttl */)
- repository.addService(TEST_CUSTOM_HOST_ID_2, TEST_CUSTOM_HOST_2, null /* ttl */)
+ repository.addServiceAndFinishProbing(TEST_CUSTOM_HOST_ID_2, TEST_CUSTOM_HOST_2)
+
+ val packet = MdnsPacket(
+ 0, /* flags */
+ emptyList(), /* questions */
+ listOf(MdnsKeyRecord(arrayOf("TestHost", "local"),
+ 0L /* receiptTimeMillis */, true /* cacheFlush */,
+ 0L /* ttlMillis */, TEST_PUBLIC_KEY),
+ ) /* answers */,
+ emptyList() /* authorityRecords */,
+ emptyList() /* additionalRecords */)
+
+ assertEquals(mapOf(TEST_CUSTOM_HOST_ID_1 to CONFLICT_HOST),
+ repository.getConflictingServices(packet))
+ }
+
+ @Test
+ fun testGetConflictingServices_customHostsReplyHasIdenticalHosts_noConflict() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
+ repository.addServiceAndFinishProbing(TEST_CUSTOM_HOST_ID_1, TEST_CUSTOM_HOST_1)
+ repository.addServiceAndFinishProbing(TEST_CUSTOM_HOST_ID_2, TEST_CUSTOM_HOST_2)
val packet = MdnsPacket(
0, /* flags */
@@ -1163,8 +1457,8 @@
@Test
fun testGetConflictingServices_customHostsCaseInsensitiveReplyHasIdenticalHosts_noConflict() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
- repository.addService(TEST_CUSTOM_HOST_ID_1, TEST_CUSTOM_HOST_1, null /* ttl */)
- repository.addService(TEST_CUSTOM_HOST_ID_2, TEST_CUSTOM_HOST_2, null /* ttl */)
+ repository.addServiceAndFinishProbing(TEST_CUSTOM_HOST_ID_1, TEST_CUSTOM_HOST_1)
+ repository.addServiceAndFinishProbing(TEST_CUSTOM_HOST_ID_2, TEST_CUSTOM_HOST_2)
val packet = MdnsPacket(
0, /* flags */
@@ -1185,6 +1479,152 @@
}
@Test
+ fun testGetConflictingServices_identicalKeyRecordsForService_noConflict() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
+
+ repository.addService(TEST_SERVICE_ID_1, NsdServiceInfo().apply {
+ serviceType = "_testservice._tcp"
+ serviceName = "MyTestService"
+ port = TEST_PORT
+ publicKey = TEST_PUBLIC_KEY
+ }, null /* ttl */)
+
+ val otherTtlMillis = 1234L
+ val packet = MdnsPacket(
+ 0 /* flags */,
+ emptyList() /* questions */,
+ listOf(
+ MdnsKeyRecord(
+ arrayOf("MyTestService", "_testservice", "_tcp", "local"),
+ 0L /* receiptTimeMillis */, true /* cacheFlush */,
+ otherTtlMillis,
+ TEST_PUBLIC_KEY)
+ ) /* answers */,
+ emptyList() /* authorityRecords */,
+ emptyList() /* additionalRecords */)
+
+ assertEquals(emptyMap(),
+ repository.getConflictingServices(packet))
+ }
+
+ @Test
+ fun testGetConflictingServices_differentKeyRecordsForService_conflict() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
+
+ repository.addService(TEST_SERVICE_ID_1, NsdServiceInfo().apply {
+ serviceType = "_testservice._tcp"
+ serviceName = "MyTestService"
+ port = TEST_PORT
+ publicKey = TEST_PUBLIC_KEY
+ }, null /* null */)
+
+ val otherTtlMillis = 1234L
+ val packet = MdnsPacket(
+ 0 /* flags */,
+ emptyList() /* questions */,
+ listOf(
+ MdnsKeyRecord(
+ arrayOf("MyTestService", "_testservice", "_tcp", "local"),
+ 0L /* receiptTimeMillis */, true /* cacheFlush */,
+ otherTtlMillis,
+ TEST_PUBLIC_KEY_2)
+ ) /* answers */,
+ emptyList() /* authorityRecords */,
+ emptyList() /* additionalRecords */)
+
+ assertEquals(mapOf(TEST_SERVICE_ID_1 to CONFLICT_SERVICE),
+ repository.getConflictingServices(packet))
+ }
+
+ @Test
+ fun testGetConflictingServices_identicalKeyRecordsForHost_noConflict() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
+
+ repository.addServiceAndFinishProbing(TEST_SERVICE_ID_1, NsdServiceInfo().apply {
+ hostname = "MyHost"
+ hostAddresses = listOf(
+ parseNumericAddress("2001:db8::1"),
+ parseNumericAddress("2001:db8::2")
+ )
+ publicKey = TEST_PUBLIC_KEY
+ })
+
+ val otherTtlMillis = 1234L
+ val packet = MdnsPacket(
+ 0 /* flags */,
+ emptyList() /* questions */,
+ listOf(
+ MdnsKeyRecord(
+ arrayOf("MyHost", "local"),
+ 0L /* receiptTimeMillis */, true /* cacheFlush */,
+ otherTtlMillis,
+ TEST_PUBLIC_KEY)
+ ) /* answers */,
+ emptyList() /* authorityRecords */,
+ emptyList() /* additionalRecords */)
+
+ assertEquals(emptyMap(),
+ repository.getConflictingServices(packet))
+ }
+
+ @Test
+ fun testGetConflictingServices_keyForCustomHostReplySameRecordName_conflictDuringProbing() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
+
+ repository.addService(TEST_SERVICE_ID_1, NsdServiceInfo().apply {
+ hostname = "MyHost"
+ publicKey = TEST_PUBLIC_KEY
+ }, null /* ttl */)
+
+ val otherTtlMillis = 1234L
+ val packet = MdnsPacket(
+ 0 /* flags */,
+ emptyList() /* questions */,
+ listOf(MdnsInetAddressRecord(arrayOf("MyHost", "local"),
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ otherTtlMillis,
+ parseNumericAddress("192.168.2.111"))
+ ) /* answers */,
+ emptyList() /* authorityRecords */,
+ emptyList() /* additionalRecords */
+ )
+
+ assertEquals(mapOf(TEST_SERVICE_ID_1 to CONFLICT_HOST),
+ repository.getConflictingServices(packet))
+ }
+
+ @Test
+ fun testGetConflictingServices_differentKeyRecordsForHost_conflict() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
+
+ repository.addService(TEST_SERVICE_ID_1, NsdServiceInfo().apply {
+ hostname = "MyHost"
+ hostAddresses = listOf(
+ parseNumericAddress("2001:db8::1"),
+ parseNumericAddress("2001:db8::2"))
+ publicKey = TEST_PUBLIC_KEY
+ }, null /* ttl */)
+
+ val otherTtlMillis = 1234L
+ val packet = MdnsPacket(
+ 0 /* flags */,
+ emptyList() /* questions */,
+ listOf(
+ MdnsKeyRecord(
+ arrayOf("MyHost", "local"),
+ 0L /* receiptTimeMillis */, true /* cacheFlush */,
+ otherTtlMillis,
+ TEST_PUBLIC_KEY_2)
+ ) /* answers */,
+ emptyList() /* authorityRecords */,
+ emptyList() /* additionalRecords */)
+
+ assertEquals(mapOf(TEST_SERVICE_ID_1 to CONFLICT_HOST),
+ repository.getConflictingServices(packet))
+ }
+
+ @Test
fun testGetConflictingServices_IdenticalService() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME, makeFlags())
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* ttl */)
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordTests.java
index 55c2846..63548c1 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordTests.java
@@ -16,11 +16,13 @@
package com.android.server.connectivity.mdns;
+import static com.android.server.connectivity.mdns.MdnsConstants.QCLASS_INTERNET;
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
@@ -424,4 +426,92 @@
assertEquals(new TextEntry("xyz", HexDump.hexStringToByteArray("FFEFDFCF")),
entries.get(2));
}
+
+ @Test
+ public void testKeyRecord() throws IOException {
+ final byte[] dataIn =
+ HexDump.hexStringToByteArray(
+ "09746573742d686f7374056c6f63616c"
+ + "00001980010000000a00440201030dc1"
+ + "41d0637960b98cbc12cfca221d2879da"
+ + "c26ee5b460e9007c992e1902d897c391"
+ + "b03764d448f7d0c772fdb03b1d9d6d52"
+ + "ff8886769e8e2362513565270962d3");
+ final byte[] rData =
+ HexDump.hexStringToByteArray(
+ "0201030dc141d0637960b98cbc12cfca"
+ + "221d2879dac26ee5b460e9007c992e19"
+ + "02d897c391b03764d448f7d0c772fdb0"
+ + "3b1d9d6d52ff8886769e8e2362513565"
+ + "270962d3");
+ assertNotNull(dataIn);
+ String dataInText = HexDump.dumpHexString(dataIn, 0, dataIn.length);
+
+ // Decode
+ DatagramPacket packet = new DatagramPacket(dataIn, dataIn.length);
+ MdnsPacketReader reader = new MdnsPacketReader(packet);
+
+ String[] name = reader.readLabels();
+ assertNotNull(name);
+ assertEquals(2, name.length);
+ String fqdn = MdnsRecord.labelsToString(name);
+ assertEquals("test-host.local", fqdn);
+
+ int type = reader.readUInt16();
+ assertEquals(MdnsRecord.TYPE_KEY, type);
+
+ MdnsKeyRecord keyRecord;
+
+ // MdnsKeyRecord(String[] name, MdnsPacketReader reader)
+ reader = new MdnsPacketReader(packet);
+ reader.readLabels(); // Skip labels
+ reader.readUInt16(); // Skip type
+ keyRecord = new MdnsKeyRecord(name, reader);
+ assertEquals(MdnsRecord.TYPE_KEY, keyRecord.getType());
+ assertTrue(keyRecord.getTtl() > 0); // Not a question so the TTL is greater than 0
+ assertTrue(keyRecord.getCacheFlush());
+ assertArrayEquals(new String[] {"test-host", "local"}, keyRecord.getName());
+ assertArrayEquals(rData, keyRecord.getRData());
+ assertNotEquals(rData, keyRecord.getRData()); // Uses a copy of the original RDATA
+ assertEquals(dataInText, toHex(keyRecord));
+
+ // MdnsKeyRecord(String[] name, MdnsPacketReader reader, boolean isQuestion)
+ reader = new MdnsPacketReader(packet);
+ reader.readLabels(); // Skip labels
+ reader.readUInt16(); // Skip type
+ keyRecord = new MdnsKeyRecord(name, reader, false /* isQuestion */);
+ assertEquals(MdnsRecord.TYPE_KEY, keyRecord.getType());
+ assertTrue(keyRecord.getTtl() > 0); // Not a question, so the TTL is greater than 0
+ assertTrue(keyRecord.getCacheFlush());
+ assertArrayEquals(new String[] {"test-host", "local"}, keyRecord.getName());
+ assertArrayEquals(rData, keyRecord.getRData());
+ assertNotEquals(rData, keyRecord.getRData()); // Uses a copy of the original RDATA
+
+ // MdnsKeyRecord(String[] name, boolean isUnicast)
+ keyRecord = new MdnsKeyRecord(name, false /* isUnicast */);
+ assertEquals(MdnsRecord.TYPE_KEY, keyRecord.getType());
+ assertEquals(0, keyRecord.getTtl());
+ assertEquals(QCLASS_INTERNET, keyRecord.getRecordClass());
+ assertFalse(keyRecord.getCacheFlush());
+ assertArrayEquals(new String[] {"test-host", "local"}, keyRecord.getName());
+ assertArrayEquals(null, keyRecord.getRData());
+
+ // MdnsKeyRecord(String[] name, long receiptTimeMillis, boolean cacheFlush, long ttlMillis,
+ // byte[] rData)
+ keyRecord =
+ new MdnsKeyRecord(
+ name,
+ 10 /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ 20_000 /* ttlMillis */,
+ rData);
+ assertEquals(MdnsRecord.TYPE_KEY, keyRecord.getType());
+ assertEquals(10, keyRecord.getReceiptTime());
+ assertTrue(keyRecord.getCacheFlush());
+ assertEquals(20_000, keyRecord.getTtl());
+ assertEquals(QCLASS_INTERNET, keyRecord.getRecordClass());
+ assertArrayEquals(new String[] {"test-host", "local"}, keyRecord.getName());
+ assertArrayEquals(rData, keyRecord.getRData());
+ assertNotEquals(rData, keyRecord.getRData()); // Uses a copy of the original RDATA
+ }
}
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSFirewallChainTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSFirewallChainTest.kt
index 16de4da..83ccccd 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSFirewallChainTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSFirewallChainTest.kt
@@ -16,7 +16,14 @@
package com.android.server
-import android.net.ConnectivityManager
+import android.net.BpfNetMapsConstants.METERED_ALLOW_CHAINS
+import android.net.BpfNetMapsConstants.METERED_DENY_CHAINS
+import android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND
+import android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW
+import android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER
+import android.net.ConnectivityManager.FIREWALL_RULE_ALLOW
+import android.net.ConnectivityManager.FIREWALL_RULE_DEFAULT
+import android.net.ConnectivityManager.FIREWALL_RULE_DENY
import android.os.Build
import androidx.test.filters.SmallTest
import com.android.server.connectivity.ConnectivityFlags.BACKGROUND_FIREWALL_CHAIN
@@ -24,6 +31,7 @@
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.assertThrows
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -52,13 +60,13 @@
@FeatureFlags(flags = [Flag(BACKGROUND_FIREWALL_CHAIN, true)])
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
fun setFirewallChainEnabled_backgroundChainEnabled_afterU() {
- cm.setFirewallChainEnabled(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, true)
- verify(bpfNetMaps).setChildChain(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, true)
+ cm.setFirewallChainEnabled(FIREWALL_CHAIN_BACKGROUND, true)
+ verify(bpfNetMaps).setChildChain(FIREWALL_CHAIN_BACKGROUND, true)
clearInvocations(bpfNetMaps)
- cm.setFirewallChainEnabled(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, false)
- verify(bpfNetMaps).setChildChain(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, false)
+ cm.setFirewallChainEnabled(FIREWALL_CHAIN_BACKGROUND, false)
+ verify(bpfNetMaps).setChildChain(FIREWALL_CHAIN_BACKGROUND, false)
}
@Test
@@ -69,10 +77,10 @@
}
private fun verifySetFirewallChainEnabledOnBackgroundDoesNothing() {
- cm.setFirewallChainEnabled(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, true)
+ cm.setFirewallChainEnabled(FIREWALL_CHAIN_BACKGROUND, true)
verify(bpfNetMaps, never()).setChildChain(anyInt(), anyBoolean())
- cm.setFirewallChainEnabled(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, false)
+ cm.setFirewallChainEnabled(FIREWALL_CHAIN_BACKGROUND, false)
verify(bpfNetMaps, never()).setChildChain(anyInt(), anyBoolean())
}
@@ -88,8 +96,8 @@
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
fun replaceFirewallChain_backgroundChainEnabled_afterU() {
val uids = intArrayOf(53, 42, 79)
- cm.replaceFirewallChain(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uids)
- verify(bpfNetMaps).replaceUidChain(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uids)
+ cm.replaceFirewallChain(FIREWALL_CHAIN_BACKGROUND, uids)
+ verify(bpfNetMaps).replaceUidChain(FIREWALL_CHAIN_BACKGROUND, uids)
}
@Test
@@ -101,7 +109,7 @@
private fun verifyReplaceFirewallChainOnBackgroundDoesNothing() {
val uids = intArrayOf(53, 42, 79)
- cm.replaceFirewallChain(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uids)
+ cm.replaceFirewallChain(FIREWALL_CHAIN_BACKGROUND, uids)
verify(bpfNetMaps, never()).replaceUidChain(anyInt(), any(IntArray::class.java))
}
@@ -118,24 +126,18 @@
fun setUidFirewallRule_backgroundChainEnabled_afterU() {
val uid = 2345
- cm.setUidFirewallRule(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uid,
- ConnectivityManager.FIREWALL_RULE_DEFAULT)
- verify(bpfNetMaps).setUidRule(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uid,
- ConnectivityManager.FIREWALL_RULE_DENY)
+ cm.setUidFirewallRule(FIREWALL_CHAIN_BACKGROUND, uid, FIREWALL_RULE_DEFAULT)
+ verify(bpfNetMaps).setUidRule(FIREWALL_CHAIN_BACKGROUND, uid, FIREWALL_RULE_DENY)
clearInvocations(bpfNetMaps)
- cm.setUidFirewallRule(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uid,
- ConnectivityManager.FIREWALL_RULE_DENY)
- verify(bpfNetMaps).setUidRule(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uid,
- ConnectivityManager.FIREWALL_RULE_DENY)
+ cm.setUidFirewallRule(FIREWALL_CHAIN_BACKGROUND, uid, FIREWALL_RULE_DENY)
+ verify(bpfNetMaps).setUidRule(FIREWALL_CHAIN_BACKGROUND, uid, FIREWALL_RULE_DENY)
clearInvocations(bpfNetMaps)
- cm.setUidFirewallRule(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uid,
- ConnectivityManager.FIREWALL_RULE_ALLOW)
- verify(bpfNetMaps).setUidRule(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uid,
- ConnectivityManager.FIREWALL_RULE_ALLOW)
+ cm.setUidFirewallRule(FIREWALL_CHAIN_BACKGROUND, uid, FIREWALL_RULE_ALLOW)
+ verify(bpfNetMaps).setUidRule(FIREWALL_CHAIN_BACKGROUND, uid, FIREWALL_RULE_ALLOW)
}
@Test
@@ -148,10 +150,49 @@
private fun verifySetUidFirewallRuleOnBackgroundDoesNothing() {
val uid = 2345
- listOf(ConnectivityManager.FIREWALL_RULE_DEFAULT, ConnectivityManager.FIREWALL_RULE_ALLOW,
- ConnectivityManager.FIREWALL_RULE_DENY).forEach { rule ->
- cm.setUidFirewallRule(ConnectivityManager.FIREWALL_CHAIN_BACKGROUND, uid, rule)
+ listOf(FIREWALL_RULE_DEFAULT, FIREWALL_RULE_ALLOW, FIREWALL_RULE_DENY).forEach { rule ->
+ cm.setUidFirewallRule(FIREWALL_CHAIN_BACKGROUND, uid, rule)
verify(bpfNetMaps, never()).setUidRule(anyInt(), anyInt(), anyInt())
}
}
+
+ @Test
+ fun testSetFirewallChainEnabled_meteredChain() {
+ (METERED_ALLOW_CHAINS + METERED_DENY_CHAINS).forEach {
+ assertThrows(UnsupportedOperationException::class.java) {
+ cm.setFirewallChainEnabled(it, true)
+ }
+ assertThrows(UnsupportedOperationException::class.java) {
+ cm.setFirewallChainEnabled(it, false)
+ }
+ }
+ }
+
+ @Test
+ fun testAddUidToMeteredNetworkAllowList() {
+ val uid = 1001
+ cm.addUidToMeteredNetworkAllowList(uid)
+ verify(bpfNetMaps).setUidRule(FIREWALL_CHAIN_METERED_ALLOW, uid, FIREWALL_RULE_ALLOW)
+ }
+
+ @Test
+ fun testRemoveUidFromMeteredNetworkAllowList() {
+ val uid = 1001
+ cm.removeUidFromMeteredNetworkAllowList(uid)
+ verify(bpfNetMaps).setUidRule(FIREWALL_CHAIN_METERED_ALLOW, uid, FIREWALL_RULE_DENY)
+ }
+
+ @Test
+ fun testAddUidToMeteredNetworkDenyList() {
+ val uid = 1001
+ cm.addUidToMeteredNetworkDenyList(uid)
+ verify(bpfNetMaps).setUidRule(FIREWALL_CHAIN_METERED_DENY_USER, uid, FIREWALL_RULE_DENY)
+ }
+
+ @Test
+ fun testRemoveUidFromMeteredNetworkDenyList() {
+ val uid = 1001
+ cm.removeUidFromMeteredNetworkDenyList(uid)
+ verify(bpfNetMaps).setUidRule(FIREWALL_CHAIN_METERED_DENY_USER, uid, FIREWALL_RULE_ALLOW)
+ }
}
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index d4f5619..6425daa 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -67,11 +67,14 @@
import static com.android.server.net.NetworkStatsEventLogger.POLL_REASON_RAT_CHANGED;
import static com.android.server.net.NetworkStatsEventLogger.PollEvent.pollReasonNameOf;
import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL;
+import static com.android.server.net.NetworkStatsService.DEFAULT_TRAFFIC_STATS_CACHE_EXPIRY_DURATION_MS;
+import static com.android.server.net.NetworkStatsService.DEFAULT_TRAFFIC_STATS_CACHE_MAX_ENTRIES;
import static com.android.server.net.NetworkStatsService.NETSTATS_FASTDATAINPUT_FALLBACKS_COUNTER_NAME;
import static com.android.server.net.NetworkStatsService.NETSTATS_FASTDATAINPUT_SUCCESSES_COUNTER_NAME;
import static com.android.server.net.NetworkStatsService.NETSTATS_IMPORT_ATTEMPTS_COUNTER_NAME;
import static com.android.server.net.NetworkStatsService.NETSTATS_IMPORT_FALLBACKS_COUNTER_NAME;
import static com.android.server.net.NetworkStatsService.NETSTATS_IMPORT_SUCCESSES_COUNTER_NAME;
+import static com.android.server.net.NetworkStatsService.TRAFFICSTATS_RATE_LIMIT_CACHE_ENABLED_FLAG;
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
import static org.junit.Assert.assertEquals;
@@ -116,6 +119,7 @@
import android.net.TestNetworkSpecifier;
import android.net.TetherStatsParcel;
import android.net.TetheringManager;
+import android.net.TrafficStats;
import android.net.UnderlyingNetworkInfo;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.net.wifi.WifiInfo;
@@ -125,6 +129,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.PowerManager;
+import android.os.Process;
import android.os.SimpleClock;
import android.provider.Settings;
import android.system.ErrnoException;
@@ -159,12 +164,15 @@
import com.android.testutils.HandlerUtils;
import com.android.testutils.TestBpfMap;
import com.android.testutils.TestableNetworkStatsProviderBinder;
+import com.android.testutils.com.android.testutils.SetFeatureFlagsRule;
+import com.android.testutils.com.android.testutils.SetFeatureFlagsRule.FeatureFlag;
import libcore.testing.io.TestIoUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -189,6 +197,7 @@
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Function;
/**
* Tests for {@link NetworkStatsService}.
@@ -202,6 +211,7 @@
// NetworkStatsService is not updatable before T, so tests do not need to be backwards compatible
@DevSdkIgnoreRule.IgnoreUpTo(SC_V2)
public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
+
private static final String TAG = "NetworkStatsServiceTest";
private static final long TEST_START = 1194220800000L;
@@ -295,6 +305,16 @@
private Boolean mIsDebuggable;
private HandlerThread mObserverHandlerThread;
final TestDependencies mDeps = new TestDependencies();
+ final HashMap<String, Boolean> mFeatureFlags = new HashMap<>();
+
+ // This will set feature flags from @FeatureFlag annotations
+ // into the map before setUp() runs.
+ @Rule
+ public final SetFeatureFlagsRule mSetFeatureFlagsRule =
+ new SetFeatureFlagsRule((name, enabled) -> {
+ mFeatureFlags.put(name, enabled);
+ return null;
+ });
private class MockContext extends BroadcastInterceptingContext {
private final Context mBaseContext;
@@ -395,8 +415,6 @@
mElapsedRealtime = 0L;
- mockDefaultSettings();
- mockNetworkStatsUidDetail(buildEmptyStats());
prepareForSystemReady();
mService.systemReady();
// Verify that system ready fetches realtime stats
@@ -432,6 +450,7 @@
class TestDependencies extends NetworkStatsService.Dependencies {
private int mCompareStatsInvocation = 0;
+ private NetworkStats.Entry mMockedTrafficStatsNativeStat = null;
@Override
public File getLegacyStatsDir() {
@@ -573,6 +592,43 @@
public boolean supportEventLogger(@NonNull Context cts) {
return true;
}
+
+ @Override
+ public boolean supportTrafficStatsRateLimitCache(Context ctx) {
+ return mFeatureFlags.getOrDefault(TRAFFICSTATS_RATE_LIMIT_CACHE_ENABLED_FLAG, false);
+ }
+
+ @Override
+ public int getTrafficStatsRateLimitCacheExpiryDuration() {
+ return DEFAULT_TRAFFIC_STATS_CACHE_EXPIRY_DURATION_MS;
+ }
+
+ @Override
+ public int getTrafficStatsRateLimitCacheMaxEntries() {
+ return DEFAULT_TRAFFIC_STATS_CACHE_MAX_ENTRIES;
+ }
+
+ @Nullable
+ @Override
+ public NetworkStats.Entry nativeGetTotalStat() {
+ return mMockedTrafficStatsNativeStat;
+ }
+
+ @Nullable
+ @Override
+ public NetworkStats.Entry nativeGetIfaceStat(String iface) {
+ return mMockedTrafficStatsNativeStat;
+ }
+
+ @Nullable
+ @Override
+ public NetworkStats.Entry nativeGetUidStat(int uid) {
+ return mMockedTrafficStatsNativeStat;
+ }
+
+ public void setNativeStat(NetworkStats.Entry entry) {
+ mMockedTrafficStatsNativeStat = entry;
+ }
}
@After
@@ -729,8 +785,6 @@
assertStatsFilesExist(true);
// boot through serviceReady() again
- mockDefaultSettings();
- mockNetworkStatsUidDetail(buildEmptyStats());
prepareForSystemReady();
mService.systemReady();
@@ -2111,8 +2165,6 @@
getLegacyCollection(PREFIX_UID_TAG, true /* includeTags */));
// Mock zero usage and boot through serviceReady(), verify there is no imported data.
- mockDefaultSettings();
- mockNetworkStatsUidDetail(buildEmptyStats());
prepareForSystemReady();
mService.systemReady();
assertStatsFilesExist(false);
@@ -2124,8 +2176,6 @@
assertStatsFilesExist(false);
// Boot through systemReady() again.
- mockDefaultSettings();
- mockNetworkStatsUidDetail(buildEmptyStats());
prepareForSystemReady();
mService.systemReady();
@@ -2199,8 +2249,6 @@
getLegacyCollection(PREFIX_UID_TAG, true /* includeTags */));
// Mock zero usage and boot through serviceReady(), verify there is no imported data.
- mockDefaultSettings();
- mockNetworkStatsUidDetail(buildEmptyStats());
prepareForSystemReady();
mService.systemReady();
assertStatsFilesExist(false);
@@ -2212,8 +2260,6 @@
assertStatsFilesExist(false);
// Boot through systemReady() again.
- mockDefaultSettings();
- mockNetworkStatsUidDetail(buildEmptyStats());
prepareForSystemReady();
mService.systemReady();
@@ -2365,6 +2411,68 @@
assertUidTotal(sTemplateWifi, UID_GREEN, 64L, 3L, 1024L, 8L, 0);
}
+ @FeatureFlag(name = TRAFFICSTATS_RATE_LIMIT_CACHE_ENABLED_FLAG, enabled = false)
+ @Test
+ public void testTrafficStatsRateLimitCache_disabled() throws Exception {
+ doTestTrafficStatsRateLimitCache(false /* cacheEnabled */);
+ }
+
+ @FeatureFlag(name = TRAFFICSTATS_RATE_LIMIT_CACHE_ENABLED_FLAG)
+ @Test
+ public void testTrafficStatsRateLimitCache_enabled() throws Exception {
+ doTestTrafficStatsRateLimitCache(true /* cacheEnabled */);
+ }
+
+ private void doTestTrafficStatsRateLimitCache(boolean cacheEnabled) throws Exception {
+ mockDefaultSettings();
+ // Calling uid is not injected into the service, use the real uid to pass the caller check.
+ final int myUid = Process.myUid();
+ mockTrafficStatsValues(64L, 3L, 1024L, 8L);
+ assertTrafficStatsValues(TEST_IFACE, myUid, 64L, 3L, 1024L, 8L);
+
+ // Verify the values are cached.
+ incrementCurrentTime(DEFAULT_TRAFFIC_STATS_CACHE_EXPIRY_DURATION_MS / 2);
+ mockTrafficStatsValues(65L, 8L, 1055L, 9L);
+ if (cacheEnabled) {
+ assertTrafficStatsValues(TEST_IFACE, myUid, 64L, 3L, 1024L, 8L);
+ } else {
+ assertTrafficStatsValues(TEST_IFACE, myUid, 65L, 8L, 1055L, 9L);
+ }
+
+ // Verify the values are updated after cache expiry.
+ incrementCurrentTime(DEFAULT_TRAFFIC_STATS_CACHE_EXPIRY_DURATION_MS);
+ assertTrafficStatsValues(TEST_IFACE, myUid, 65L, 8L, 1055L, 9L);
+ }
+
+ private void mockTrafficStatsValues(long rxBytes, long rxPackets,
+ long txBytes, long txPackets) {
+ // In practice, keys and operations are not used and filled with default values when
+ // returned by JNI layer.
+ final NetworkStats.Entry entry = new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT,
+ TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
+ rxBytes, rxPackets, txBytes, txPackets, 0L);
+ mDeps.setNativeStat(entry);
+ }
+
+ // Assert for 3 different API return values respectively.
+ private void assertTrafficStatsValues(String iface, int uid, long rxBytes, long rxPackets,
+ long txBytes, long txPackets) {
+ assertTrafficStatsValuesThat(rxBytes, rxPackets, txBytes, txPackets,
+ (type) -> mService.getTotalStats(type));
+ assertTrafficStatsValuesThat(rxBytes, rxPackets, txBytes, txPackets,
+ (type) -> mService.getIfaceStats(iface, type));
+ assertTrafficStatsValuesThat(rxBytes, rxPackets, txBytes, txPackets,
+ (type) -> mService.getUidStats(uid, type));
+ }
+
+ private void assertTrafficStatsValuesThat(long rxBytes, long rxPackets, long txBytes,
+ long txPackets, Function<Integer, Long> fetcher) {
+ assertEquals(rxBytes, (long) fetcher.apply(TrafficStats.TYPE_RX_BYTES));
+ assertEquals(rxPackets, (long) fetcher.apply(TrafficStats.TYPE_RX_PACKETS));
+ assertEquals(txBytes, (long) fetcher.apply(TrafficStats.TYPE_TX_BYTES));
+ assertEquals(txPackets, (long) fetcher.apply(TrafficStats.TYPE_TX_PACKETS));
+ }
+
private void assertShouldRunComparison(boolean expected, boolean isDebuggable) {
assertEquals("shouldRunComparison (debuggable=" + isDebuggable + "): ",
expected, mService.shouldRunComparison());
@@ -2429,6 +2537,8 @@
}
private void prepareForSystemReady() throws Exception {
+ mockDefaultSettings();
+ mockNetworkStatsUidDetail(buildEmptyStats());
mockNetworkStatsSummary(buildEmptyStats());
}
diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
index 63cd574..0559499 100644
--- a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
+++ b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
@@ -73,7 +73,6 @@
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.InetAddresses;
-import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.LocalNetworkConfig;
import android.net.LocalNetworkInfo;
@@ -106,7 +105,6 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.UserManager;
import android.util.Log;
import android.util.SparseArray;
@@ -129,8 +127,6 @@
import java.io.IOException;
import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.time.Instant;
@@ -165,6 +161,9 @@
// Note that this regex allows "XX:XX-XX" as well but we don't need to be a strict checker
private static final String OUI_REGEX = "^([0-9A-Fa-f]{2}[:-]?){2}([0-9A-Fa-f]{2})$";
+ // The channel mask that indicates all channels from channel 11 to channel 24
+ private static final int CHANNEL_MASK_11_TO_24 = 0x1FFF800;
+
// Below member fields can be accessed from both the binder and handler threads
private final Context mContext;
@@ -260,38 +259,6 @@
countryCodeSupplier);
}
- private static Inet6Address bytesToInet6Address(byte[] addressBytes) {
- try {
- return (Inet6Address) Inet6Address.getByAddress(addressBytes);
- } catch (UnknownHostException e) {
- // This is unlikely to happen unless the Thread daemon is critically broken
- return null;
- }
- }
-
- private static InetAddress addressInfoToInetAddress(Ipv6AddressInfo addressInfo) {
- return bytesToInet6Address(addressInfo.address);
- }
-
- private static LinkAddress newLinkAddress(Ipv6AddressInfo addressInfo) {
- long deprecationTimeMillis =
- addressInfo.isPreferred
- ? LinkAddress.LIFETIME_PERMANENT
- : SystemClock.elapsedRealtime();
-
- InetAddress address = addressInfoToInetAddress(addressInfo);
-
- // flags and scope will be adjusted automatically depending on the address and
- // its lifetimes.
- return new LinkAddress(
- address,
- addressInfo.prefixLength,
- 0 /* flags */,
- 0 /* scope */,
- deprecationTimeMillis,
- LinkAddress.LIFETIME_PERMANENT /* expirationTime */);
- }
-
private NetworkRequest newUpstreamNetworkRequest() {
NetworkRequest.Builder builder = new NetworkRequest.Builder().clearCapabilities();
@@ -826,6 +793,14 @@
private static int selectChannel(
int supportedChannelMask, int preferredChannelMask, Random random) {
+ // Due to radio hardware performance reasons, many Thread radio chips need to reduce their
+ // transmit power on edge channels to pass regulatory RF certification. Thread edge channel
+ // 25 and 26 are not preferred here.
+ //
+ // If users want to use channel 25 or 26, they can change the channel via the method
+ // ActiveOperationalDataset.Builder(activeOperationalDataset).setChannel(channel).build().
+ preferredChannelMask = preferredChannelMask & CHANNEL_MASK_11_TO_24;
+
// If the preferred channel mask is not empty, select a random channel from it, otherwise
// choose one from the supported channel mask.
preferredChannelMask = preferredChannelMask & supportedChannelMask;
@@ -1172,20 +1147,10 @@
}
}
- private void handleAddressChanged(Ipv6AddressInfo addressInfo, boolean isAdded) {
+ private void handleAddressChanged(List<Ipv6AddressInfo> addressInfoList) {
checkOnHandlerThread();
- InetAddress address = addressInfoToInetAddress(addressInfo);
- if (address.isMulticastAddress()) {
- Log.i(TAG, "Ignoring multicast address " + address.getHostAddress());
- return;
- }
- LinkAddress linkAddress = newLinkAddress(addressInfo);
- if (isAdded) {
- mTunIfController.addAddress(linkAddress);
- } else {
- mTunIfController.removeAddress(linkAddress);
- }
+ mTunIfController.updateAddresses(addressInfoList);
// The OT daemon can send link property updates before the networkAgent is
// registered
@@ -1534,8 +1499,8 @@
}
@Override
- public void onAddressChanged(Ipv6AddressInfo addressInfo, boolean isAdded) {
- mHandler.post(() -> handleAddressChanged(addressInfo, isAdded));
+ public void onAddressChanged(List<Ipv6AddressInfo> addressInfoList) {
+ mHandler.post(() -> handleAddressChanged(addressInfoList));
}
@Override
diff --git a/thread/service/java/com/android/server/thread/TunInterfaceController.java b/thread/service/java/com/android/server/thread/TunInterfaceController.java
index b29a54f..dec72b2 100644
--- a/thread/service/java/com/android/server/thread/TunInterfaceController.java
+++ b/thread/service/java/com/android/server/thread/TunInterfaceController.java
@@ -16,6 +16,8 @@
package com.android.server.thread;
+import static android.system.OsConstants.EADDRINUSE;
+
import android.annotation.Nullable;
import android.net.IpPrefix;
import android.net.LinkAddress;
@@ -29,12 +31,23 @@
import android.system.OsConstants;
import android.util.Log;
+import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
import com.android.net.module.util.netlink.NetlinkUtils;
import com.android.net.module.util.netlink.RtNetlinkAddressMessage;
+import com.android.server.thread.openthread.Ipv6AddressInfo;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InterruptedIOException;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.MulticastSocket;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
/** Controller for virtual/tunnel network interfaces. */
public class TunInterfaceController {
@@ -51,12 +64,16 @@
private ParcelFileDescriptor mParcelTunFd;
private FileDescriptor mNetlinkSocket;
private static int sNetlinkSeqNo = 0;
+ private final MulticastSocket mMulticastSocket; // For join group and leave group
+ private NetworkInterface mNetworkInterface;
+ private List<InetAddress> mMulticastAddresses = new ArrayList<>();
/** Creates a new {@link TunInterfaceController} instance for given interface. */
public TunInterfaceController(String interfaceName) {
mIfName = interfaceName;
mLinkProperties.setInterfaceName(mIfName);
mLinkProperties.setMtu(MTU);
+ mMulticastSocket = createMulticastSocket();
}
/** Returns link properties of the Thread TUN interface. */
@@ -76,6 +93,11 @@
} catch (ErrnoException e) {
throw new IOException("Failed to create netlink socket", e);
}
+ try {
+ mNetworkInterface = NetworkInterface.getByName(mIfName);
+ } catch (SocketException e) {
+ throw new IOException("Failed to get NetworkInterface", e);
+ }
}
public void destroyTunInterface() {
@@ -87,6 +109,7 @@
}
mParcelTunFd = null;
mNetlinkSocket = null;
+ mNetworkInterface = null;
}
/** Returns the FD of the tunnel interface. */
@@ -178,6 +201,48 @@
}
}
+ public void updateAddresses(List<Ipv6AddressInfo> addressInfoList) {
+ final List<LinkAddress> newLinkAddresses = new ArrayList<>();
+ final List<InetAddress> newMulticastAddresses = new ArrayList<>();
+ boolean hasActiveOmrAddress = false;
+
+ for (Ipv6AddressInfo addressInfo : addressInfoList) {
+ if (addressInfo.isActiveOmr) {
+ hasActiveOmrAddress = true;
+ break;
+ }
+ }
+
+ for (Ipv6AddressInfo addressInfo : addressInfoList) {
+ InetAddress address = addressInfoToInetAddress(addressInfo);
+ if (address.isMulticastAddress()) {
+ newMulticastAddresses.add(address);
+ } else {
+ newLinkAddresses.add(newLinkAddress(addressInfo, hasActiveOmrAddress));
+ }
+ }
+
+ final CompareResult<LinkAddress> addressDiff =
+ new CompareResult<>(mLinkProperties.getAllLinkAddresses(), newLinkAddresses);
+ for (LinkAddress linkAddress : addressDiff.removed) {
+ removeAddress(linkAddress);
+ }
+ for (LinkAddress linkAddress : addressDiff.added) {
+ addAddress(linkAddress);
+ }
+
+ final CompareResult<InetAddress> multicastAddressDiff =
+ new CompareResult<>(mMulticastAddresses, newMulticastAddresses);
+ for (InetAddress address : multicastAddressDiff.removed) {
+ leaveGroup(address);
+ }
+ for (InetAddress address : multicastAddressDiff.added) {
+ joinGroup(address);
+ }
+ mMulticastAddresses.clear();
+ mMulticastAddresses.addAll(newMulticastAddresses);
+ }
+
private RouteInfo getRouteForAddress(LinkAddress linkAddress) {
return new RouteInfo(
new IpPrefix(linkAddress.getAddress(), linkAddress.getPrefixLength()),
@@ -195,4 +260,77 @@
Log.e(TAG, "Failed to set Thread TUN interface down");
}
}
+
+ private static InetAddress addressInfoToInetAddress(Ipv6AddressInfo addressInfo) {
+ return bytesToInet6Address(addressInfo.address);
+ }
+
+ private static Inet6Address bytesToInet6Address(byte[] addressBytes) {
+ try {
+ return (Inet6Address) Inet6Address.getByAddress(addressBytes);
+ } catch (UnknownHostException e) {
+ // This is unlikely to happen unless the Thread daemon is critically broken
+ return null;
+ }
+ }
+
+ private static LinkAddress newLinkAddress(
+ Ipv6AddressInfo addressInfo, boolean hasActiveOmrAddress) {
+ // Mesh-local addresses and OMR address have the same scope, to distinguish them we set
+ // mesh-local addresses as deprecated when there is an active OMR address.
+ // For OMR address and link-local address we only use the value isPreferred set by
+ // ot-daemon.
+ boolean isPreferred = addressInfo.isPreferred;
+ if (addressInfo.isMeshLocal && hasActiveOmrAddress) {
+ isPreferred = false;
+ }
+
+ final long deprecationTimeMillis =
+ isPreferred ? LinkAddress.LIFETIME_PERMANENT : SystemClock.elapsedRealtime();
+
+ final InetAddress address = addressInfoToInetAddress(addressInfo);
+
+ // flags and scope will be adjusted automatically depending on the address and
+ // its lifetimes.
+ return new LinkAddress(
+ address,
+ addressInfo.prefixLength,
+ 0 /* flags */,
+ 0 /* scope */,
+ deprecationTimeMillis,
+ LinkAddress.LIFETIME_PERMANENT /* expirationTime */);
+ }
+
+ private MulticastSocket createMulticastSocket() {
+ try {
+ return new MulticastSocket();
+ } catch (IOException e) {
+ throw new IllegalStateException("Failed to create multicast socket ", e);
+ }
+ }
+
+ private void joinGroup(InetAddress address) {
+ InetSocketAddress socketAddress = new InetSocketAddress(address, 0);
+ try {
+ mMulticastSocket.joinGroup(socketAddress, mNetworkInterface);
+ } catch (IOException e) {
+ if (e.getCause() instanceof ErrnoException) {
+ ErrnoException ee = (ErrnoException) e.getCause();
+ if (ee.errno == EADDRINUSE) {
+ Log.w(TAG, "Already joined group" + address.getHostAddress(), e);
+ return;
+ }
+ }
+ Log.e(TAG, "failed to join group " + address.getHostAddress(), e);
+ }
+ }
+
+ private void leaveGroup(InetAddress address) {
+ InetSocketAddress socketAddress = new InetSocketAddress(address, 0);
+ try {
+ mMulticastSocket.leaveGroup(socketAddress, mNetworkInterface);
+ } catch (IOException e) {
+ Log.e(TAG, "failed to leave group " + address.getHostAddress(), e);
+ }
+ }
}
diff --git a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
index ba7392c..dea4279 100644
--- a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
@@ -67,6 +67,7 @@
import android.net.thread.utils.TapTestNetworkTracker;
import android.net.thread.utils.ThreadFeatureCheckerRule;
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
+import android.os.Build;
import android.os.HandlerThread;
import android.os.OutcomeReceiver;
@@ -79,6 +80,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
@@ -749,17 +751,6 @@
}
@Test
- public void setEnabled_toggleAfterJoin_joinsThreadNetworkAgain() throws Exception {
- joinRandomizedDatasetAndWait(mController);
-
- setEnabledAndWait(mController, false);
- assertThat(getDeviceRole(mController)).isEqualTo(DEVICE_ROLE_STOPPED);
- setEnabledAndWait(mController, true);
-
- runAsShell(ACCESS_NETWORK_STATE, () -> waitForAttachedState(mController));
- }
-
- @Test
public void setEnabled_enableFollowedByDisable_allSucceed() throws Exception {
joinRandomizedDatasetAndWait(mController);
CompletableFuture<Void> setFuture1 = new CompletableFuture<>();
@@ -792,10 +783,14 @@
public void threadNetworkCallback_deviceAttached_threadNetworkIsAvailable() throws Exception {
CompletableFuture<Network> networkFuture = new CompletableFuture<>();
ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
- NetworkRequest networkRequest =
- new NetworkRequest.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_THREAD)
- .build();
+ NetworkRequest.Builder networkRequestBuilder =
+ new NetworkRequest.Builder().addTransportType(NetworkCapabilities.TRANSPORT_THREAD);
+ // Before V, we need to explicitly set `NET_CAPABILITY_LOCAL_NETWORK` capability to request
+ // a Thread network.
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ networkRequestBuilder.addCapability(NET_CAPABILITY_LOCAL_NETWORK);
+ }
+ NetworkRequest networkRequest = networkRequestBuilder.build();
ConnectivityManager.NetworkCallback networkCallback =
new ConnectivityManager.NetworkCallback() {
@Override
@@ -857,6 +852,7 @@
}
@Test
+ @Ignore("b/333649897: Enable this when it's not flaky at all")
public void meshcopService_joinedNetwork_discoveredHasNetwork() throws Exception {
setUpTestNetwork();
diff --git a/thread/tests/integration/Android.bp b/thread/tests/integration/Android.bp
index 94985b1..71693af 100644
--- a/thread/tests/integration/Android.bp
+++ b/thread/tests/integration/Android.bp
@@ -34,6 +34,7 @@
"testables",
"ThreadNetworkTestUtils",
"truth",
+ "ot-daemon-aidl-java",
],
libs: [
"android.test.runner",
diff --git a/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java b/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java
index 9b1c338..8c63d37 100644
--- a/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java
+++ b/thread/tests/integration/src/android/net/thread/BorderRoutingTest.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
import static android.net.thread.utils.IntegrationTestUtils.JOIN_TIMEOUT;
+import static android.net.thread.utils.IntegrationTestUtils.getIpv6LinkAddresses;
import static android.net.thread.utils.IntegrationTestUtils.isExpectedIcmpv6Packet;
import static android.net.thread.utils.IntegrationTestUtils.isFromIpv6Source;
import static android.net.thread.utils.IntegrationTestUtils.isInMulticastGroup;
@@ -33,6 +34,7 @@
import static com.android.testutils.TestPermissionUtil.runAsShell;
import static com.google.common.io.BaseEncoding.base16;
+import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -43,6 +45,8 @@
import android.content.Context;
import android.net.InetAddresses;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.MacAddress;
import android.net.thread.utils.FullThreadDevice;
@@ -55,6 +59,7 @@
import android.net.thread.utils.ThreadNetworkControllerWrapper;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.SystemClock;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.LargeTest;
@@ -252,6 +257,21 @@
}
@Test
+ public void unicastRouting_meshLocalAddressesAreNotPreferred() throws Exception {
+ // When BR is enabled, there will be OMR address, so the mesh-local addresses are expected
+ // to be deprecated.
+ List<LinkAddress> linkAddresses = getIpv6LinkAddresses("thread-wpan");
+ IpPrefix meshLocalPrefix = DEFAULT_DATASET.getMeshLocalPrefix();
+
+ for (LinkAddress address : linkAddresses) {
+ if (meshLocalPrefix.contains(address.getAddress())) {
+ assertThat(address.getDeprecationTime()).isAtMost(SystemClock.elapsedRealtime());
+ assertThat(address.isPreferred()).isFalse();
+ }
+ }
+ }
+
+ @Test
@RequiresIpv6MulticastRouting
public void multicastRouting_ftdSubscribedMulticastAddress_infraLinkJoinsMulticastGroup()
throws Exception {
diff --git a/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java b/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java
index 5a8d21f..e10f134 100644
--- a/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java
+++ b/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java
@@ -265,6 +265,7 @@
}
@Test
+ @Ignore("TODO: b/333806992 - Enable when it's not flaky at all")
public void advertisingProxy_srpClientUnregistersService_serviceIsNotDiscoverableByMdns()
throws Exception {
/*
diff --git a/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java b/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java
index 8f8aa5f..998e70d 100644
--- a/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java
+++ b/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java
@@ -21,15 +21,22 @@
import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_STOPPED;
import static android.net.thread.utils.IntegrationTestUtils.CALLBACK_TIMEOUT;
import static android.net.thread.utils.IntegrationTestUtils.RESTART_JOIN_TIMEOUT;
+import static android.net.thread.utils.IntegrationTestUtils.getIpv6LinkAddresses;
+import static android.net.thread.utils.IntegrationTestUtils.isInMulticastGroup;
import static android.net.thread.utils.IntegrationTestUtils.waitFor;
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
+import static com.android.server.thread.openthread.IOtDaemon.TUN_IF_NAME;
import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import android.content.Context;
+import android.net.InetAddresses;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
import android.net.thread.utils.FullThreadDevice;
import android.net.thread.utils.OtDaemonController;
import android.net.thread.utils.ThreadFeatureCheckerRule;
@@ -80,6 +87,9 @@
private static final ActiveOperationalDataset DEFAULT_DATASET =
ActiveOperationalDataset.fromThreadTlvs(DEFAULT_DATASET_TLVS);
+ private static final Inet6Address GROUP_ADDR_ALL_ROUTERS =
+ (Inet6Address) InetAddresses.parseNumericAddress("ff02::2");
+
@Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
private ExecutorService mExecutor;
@@ -201,6 +211,48 @@
assertThat(udpReply).isEqualTo("Hello,Thread");
}
+ @Test
+ public void joinNetworkWithBrDisabled_meshLocalAddressesArePreferred() throws Exception {
+ // When BR feature is disabled, there is no OMR address, so the mesh-local addresses are
+ // expected to be preferred.
+ mOtCtl.executeCommand("br disable");
+ mController.joinAndWait(DEFAULT_DATASET);
+
+ IpPrefix meshLocalPrefix = DEFAULT_DATASET.getMeshLocalPrefix();
+ List<LinkAddress> linkAddresses = getIpv6LinkAddresses("thread-wpan");
+ for (LinkAddress address : linkAddresses) {
+ if (meshLocalPrefix.contains(address.getAddress())) {
+ assertThat(address.getDeprecationTime())
+ .isGreaterThan(SystemClock.elapsedRealtime());
+ assertThat(address.isPreferred()).isTrue();
+ }
+ }
+
+ mOtCtl.executeCommand("br enable");
+ }
+
+ @Test
+ public void joinNetwork_tunInterfaceJoinsAllRouterMulticastGroup() throws Exception {
+ mController.joinAndWait(DEFAULT_DATASET);
+
+ assertTunInterfaceMemberOfGroup(GROUP_ADDR_ALL_ROUTERS);
+ }
+
+ @Test
+ public void edPingsMeshLocalAddresses_oneReplyPerRequest() throws Exception {
+ mController.joinAndWait(DEFAULT_DATASET);
+ startFtdChild(mFtd, DEFAULT_DATASET);
+ List<Inet6Address> meshLocalAddresses = mOtCtl.getMeshLocalAddresses();
+
+ for (Inet6Address address : meshLocalAddresses) {
+ assertWithMessage(
+ "There may be duplicated replies of ping request to "
+ + address.getHostAddress())
+ .that(mFtd.ping(address, 2))
+ .isEqualTo(2);
+ }
+ }
+
// TODO (b/323300829): add more tests for integration with linux platform and
// ConnectivityService
@@ -236,4 +288,8 @@
throw new IllegalStateException(e);
}
}
+
+ private void assertTunInterfaceMemberOfGroup(Inet6Address address) throws Exception {
+ waitFor(() -> isInMulticastGroup(TUN_IF_NAME, address), TUN_ADDR_UPDATE_TIMEOUT);
+ }
}
diff --git a/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java b/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
index 5e70f6c..9370ee3 100644
--- a/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
+++ b/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
@@ -422,7 +422,21 @@
PING_TIMEOUT_SECONDS);
}
- private void ping(
+ /** Returns the number of ping reply packets received. */
+ public int ping(Inet6Address address, int count) {
+ List<String> output =
+ ping(
+ address,
+ null,
+ PING_SIZE,
+ count,
+ PING_INTERVAL,
+ HOP_LIMIT,
+ PING_TIMEOUT_SECONDS);
+ return getReceivedPacketsCount(output);
+ }
+
+ private List<String> ping(
Inet6Address address,
Inet6Address source,
int size,
@@ -445,7 +459,21 @@
+ hopLimit
+ " "
+ timeout;
- executeCommand(cmd);
+ return executeCommand(cmd);
+ }
+
+ private int getReceivedPacketsCount(List<String> stringList) {
+ Pattern pattern = Pattern.compile("([\\d]+) packets received");
+
+ for (String message : stringList) {
+ Matcher matcher = pattern.matcher(message);
+ if (matcher.find()) {
+ String packetCountStr = matcher.group(1);
+ return Integer.parseInt(packetCountStr);
+ }
+ }
+ // No match found
+ return -1;
}
@FormatMethod
diff --git a/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java b/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java
index 3efdf7d..9be9566 100644
--- a/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java
+++ b/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java
@@ -25,6 +25,8 @@
import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import android.net.InetAddresses;
+import android.net.LinkAddress;
import android.net.TestNetworkInterface;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
@@ -291,6 +293,20 @@
return false;
}
+ public static List<LinkAddress> getIpv6LinkAddresses(String interfaceName) throws IOException {
+ List<LinkAddress> addresses = new ArrayList<>();
+ final String cmd = " ip -6 addr show dev " + interfaceName;
+ final String output = runShellCommandOrThrow(cmd);
+
+ for (final String line : output.split("\\n")) {
+ if (line.contains("inet6")) {
+ addresses.add(parseAddressLine(line));
+ }
+ }
+
+ return addresses;
+ }
+
/** Return the first discovered service of {@code serviceType}. */
public static NsdServiceInfo discoverService(NsdManager nsdManager, String serviceType)
throws Exception {
@@ -392,4 +408,29 @@
@Override
public void onServiceInfoCallbackUnregistered() {}
}
+
+ /**
+ * Parses a line of output from "ip -6 addr show" into a {@link LinkAddress}.
+ *
+ * <p>Example line: "inet6 2001:db8:1:1::1/64 scope global deprecated"
+ */
+ private static LinkAddress parseAddressLine(String line) {
+ String[] parts = line.trim().split("\\s+");
+ String addressString = parts[1];
+ String[] pieces = addressString.split("/", 2);
+ int prefixLength = Integer.parseInt(pieces[1]);
+ final InetAddress address = InetAddresses.parseNumericAddress(pieces[0]);
+ long deprecationTimeMillis =
+ line.contains("deprecated")
+ ? SystemClock.elapsedRealtime()
+ : LinkAddress.LIFETIME_PERMANENT;
+
+ return new LinkAddress(
+ address,
+ prefixLength,
+ 0 /* flags */,
+ 0 /* scope */,
+ deprecationTimeMillis,
+ LinkAddress.LIFETIME_PERMANENT /* expirationTime */);
+ }
}
diff --git a/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java b/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
index 85b6873..493058f 100644
--- a/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
+++ b/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
@@ -115,7 +115,11 @@
private static final String DEFAULT_NETWORK_NAME = "thread-wpan0";
private static final int OT_ERROR_NONE = 0;
private static final int DEFAULT_SUPPORTED_CHANNEL_MASK = 0x07FFF800; // from channel 11 to 26
- private static final int DEFAULT_PREFERRED_CHANNEL_MASK = 0x00000800; // channel 11
+
+ // The DEFAULT_PREFERRED_CHANNEL_MASK is the ot-daemon preferred channel mask. Channel 25 and
+ // 26 are not preferred by dataset. The ThreadNetworkControllerService will only select channel
+ // 11 when it creates randomized dataset.
+ private static final int DEFAULT_PREFERRED_CHANNEL_MASK = 0x06000800; // channel 11, 25 and 26
private static final int DEFAULT_SELECTED_CHANNEL = 11;
private static final byte[] DEFAULT_SUPPORTED_CHANNEL_MASK_ARRAY = base16().decode("001FFFE0");