Snap for 8146118 from 555c9d7be3136737aa1a0185f2d0f73db4633101 to mainline-permission-release
Change-Id: I0feb6803957ee8b068dd6b3cf1360435d964d521
diff --git a/common/Android.bp b/common/Android.bp
index 50cfb02..940f1d6 100644
--- a/common/Android.bp
+++ b/common/Android.bp
@@ -111,21 +111,24 @@
name: "net-utils-device-common-bpf",
srcs: [
"device/com/android/net/module/util/BpfMap.java",
+ "device/com/android/net/module/util/HexDump.java",
+ "device/com/android/net/module/util/IBpfMap.java",
"device/com/android/net/module/util/JniUtil.java",
+ "device/com/android/net/module/util/Struct.java",
+ "device/com/android/net/module/util/TcUtils.java",
],
- sdk_version: "system_current",
+ sdk_version: "module_current",
min_sdk_version: "29",
visibility: [
"//frameworks/libs/net/common/tests:__subpackages__",
"//frameworks/libs/net/common/testutils:__subpackages__",
"//packages/modules/Connectivity:__subpackages__",
"//packages/modules/NetworkStack:__subpackages__",
- ],
- static_libs: [
- "net-utils-device-common-struct",
+ "//frameworks/base/services/core",
],
libs: [
"androidx.annotation_annotation",
+ "framework-connectivity.stubs.module_lib",
],
apex_available: [
"com.android.tethering",
@@ -143,7 +146,7 @@
"device/com/android/net/module/util/Struct.java",
"device/com/android/net/module/util/structs/*.java",
],
- sdk_version: "system_current",
+ sdk_version: "module_current",
min_sdk_version: "29",
visibility: [
"//frameworks/libs/net/common/testutils:__subpackages__",
@@ -155,6 +158,7 @@
],
libs: [
"androidx.annotation_annotation",
+ "framework-connectivity.stubs.module_lib",
],
apex_available: [
"com.android.tethering",
@@ -168,7 +172,7 @@
srcs: [
"device/com/android/net/module/util/netlink/*.java",
],
- sdk_version: "system_current",
+ sdk_version: "module_current",
min_sdk_version: "29",
visibility: [
"//frameworks/libs/net/common/testutils:__subpackages__",
@@ -180,6 +184,7 @@
],
libs: [
"androidx.annotation_annotation",
+ "framework-connectivity.stubs.module_lib",
],
apex_available: [
"com.android.tethering",
@@ -223,11 +228,12 @@
name: "net-utils-framework-common",
srcs: [
":net-utils-framework-common-srcs",
- // TODO: avoid including all framework annotations as they end up in library users jars
- // and need jarjaring
- ":framework-annotations",
],
- sdk_version: "system_current",
+ sdk_version: "module_current",
+ libs: [
+ "framework-annotations-lib",
+ "framework-connectivity.stubs.module_lib",
+ ],
jarjar_rules: "jarjar-rules-shared.txt",
visibility: [
"//cts/tests/tests/net",
@@ -246,6 +252,7 @@
"//frameworks/libs/net/common/tests:__subpackages__",
"//frameworks/libs/net/common/device",
"//packages/modules/Wifi/framework/tests:__subpackages__",
+ "//packages/apps/Settings",
],
lint: { strict_updatability_linting: true },
}
@@ -277,6 +284,7 @@
"framework-connectivity",
],
visibility: [
+ // TODO: remove after NetworkStatsService moves to the module.
"//frameworks/base/services/net",
"//packages/modules/Connectivity/tests:__subpackages__",
],
diff --git a/common/device/com/android/net/module/util/BpfMap.java b/common/device/com/android/net/module/util/BpfMap.java
index 5f05c7c..b42c388 100644
--- a/common/device/com/android/net/module/util/BpfMap.java
+++ b/common/device/com/android/net/module/util/BpfMap.java
@@ -40,7 +40,7 @@
* @param <K> the key of the map.
* @param <V> the value of the map.
*/
-public class BpfMap<K extends Struct, V extends Struct> implements AutoCloseable {
+public class BpfMap<K extends Struct, V extends Struct> implements IBpfMap<K, V>, AutoCloseable {
static {
System.loadLibrary(JniUtil.getJniLibraryName(BpfMap.class.getPackage()));
}
@@ -100,6 +100,7 @@
* Update an existing or create a new key -> value entry in an eBbpf map.
* (use insertOrReplaceEntry() if you need to know whether insert or replace happened)
*/
+ @Override
public void updateEntry(K key, V value) throws ErrnoException {
writeToMapEntry(mMapFd, key.writeToBytes(), value.writeToBytes(), BPF_ANY);
}
@@ -108,6 +109,7 @@
* If the key does not exist in the map, insert key -> value entry into eBpf map.
* Otherwise IllegalStateException will be thrown.
*/
+ @Override
public void insertEntry(K key, V value)
throws ErrnoException, IllegalStateException {
try {
@@ -123,6 +125,7 @@
* If the key already exists in the map, replace its value. Otherwise NoSuchElementException
* will be thrown.
*/
+ @Override
public void replaceEntry(K key, V value)
throws ErrnoException, NoSuchElementException {
try {
@@ -140,6 +143,7 @@
* (use updateEntry() if you don't care whether insert or replace happened)
* Note: see inline comment below if running concurrently with delete operations.
*/
+ @Override
public boolean insertOrReplaceEntry(K key, V value)
throws ErrnoException {
try {
@@ -164,11 +168,13 @@
}
/** Remove existing key from eBpf map. Return false if map was not modified. */
+ @Override
public boolean deleteEntry(K key) throws ErrnoException {
return deleteMapEntry(mMapFd, key.writeToBytes());
}
/** Returns {@code true} if this map contains no elements. */
+ @Override
public boolean isEmpty() throws ErrnoException {
return getFirstKey() == null;
}
@@ -189,6 +195,7 @@
*
* TODO: consider allowing null passed-in key.
*/
+ @Override
public K getNextKey(@NonNull K key) throws ErrnoException {
Objects.requireNonNull(key);
return getNextKeyInternal(key);
@@ -202,11 +209,13 @@
}
/** Get the first key of eBpf map. */
+ @Override
public K getFirstKey() throws ErrnoException {
return getNextKeyInternal(null);
}
/** Check whether a key exists in the map. */
+ @Override
public boolean containsKey(@NonNull K key) throws ErrnoException {
Objects.requireNonNull(key);
@@ -215,6 +224,7 @@
}
/** Retrieve a value from the map. Return null if there is no such key. */
+ @Override
public V getValue(@NonNull K key) throws ErrnoException {
Objects.requireNonNull(key);
final byte[] rawValue = getRawValue(key.writeToBytes());
@@ -239,6 +249,7 @@
* other structural modifications to the map, such as adding entries or deleting other entries.
* Otherwise, iteration will result in undefined behaviour.
*/
+ @Override
public void forEach(BiConsumer<K, V> action) throws ErrnoException {
@Nullable K nextKey = getFirstKey();
@@ -262,6 +273,7 @@
* @throws ErrnoException if the map is already closed, if an error occurred during iteration,
* or if a non-ENOENT error occurred when deleting a key.
*/
+ @Override
public void clear() throws ErrnoException {
K key = getFirstKey();
while (key != null) {
diff --git a/common/device/com/android/net/module/util/IBpfMap.java b/common/device/com/android/net/module/util/IBpfMap.java
new file mode 100644
index 0000000..708cf61
--- /dev/null
+++ b/common/device/com/android/net/module/util/IBpfMap.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.net.module.util;
+
+import android.system.ErrnoException;
+
+import androidx.annotation.NonNull;
+
+import java.util.NoSuchElementException;
+import java.util.function.BiConsumer;
+
+/**
+ * The interface of BpfMap. This could be used to inject for testing.
+ * So the testing code won't load the JNI and update the entries to kernel.
+ *
+ * @param <K> the key of the map.
+ * @param <V> the value of the map.
+ */
+public interface IBpfMap<K extends Struct, V extends Struct> {
+ /** Update an existing or create a new key -> value entry in an eBbpf map. */
+ void updateEntry(K key, V value) throws ErrnoException;
+
+ /** If the key does not exist in the map, insert key -> value entry into eBpf map. */
+ void insertEntry(K key, V value) throws ErrnoException, IllegalStateException;
+
+ /** If the key already exists in the map, replace its value. */
+ void replaceEntry(K key, V value) throws ErrnoException, NoSuchElementException;
+
+ /**
+ * Update an existing or create a new key -> value entry in an eBbpf map. Returns true if
+ * inserted, false if replaced. (use updateEntry() if you don't care whether insert or replace
+ * happened).
+ */
+ boolean insertOrReplaceEntry(K key, V value) throws ErrnoException;
+
+ /** Remove existing key from eBpf map. Return true if something was deleted. */
+ boolean deleteEntry(K key) throws ErrnoException;
+
+ /** Returns {@code true} if this map contains no elements. */
+ boolean isEmpty() throws ErrnoException;
+
+ /** Get the key after the passed-in key. */
+ K getNextKey(@NonNull K key) throws ErrnoException;
+
+ /** Get the first key of the eBpf map. */
+ K getFirstKey() throws ErrnoException;
+
+ /** Check whether a key exists in the map. */
+ boolean containsKey(@NonNull K key) throws ErrnoException;
+
+ /** Retrieve a value from the map. */
+ V getValue(@NonNull K key) throws ErrnoException;
+
+ /**
+ * Iterate through the map and handle each key -> value retrieved base on the given BiConsumer.
+ */
+ void forEach(BiConsumer<K, V> action) throws ErrnoException;
+
+ /** Clears the map. */
+ void clear() throws ErrnoException;
+}
diff --git a/common/device/com/android/net/module/util/Struct.java b/common/device/com/android/net/module/util/Struct.java
index b43e2c4..d717bc7 100644
--- a/common/device/com/android/net/module/util/Struct.java
+++ b/common/device/com/android/net/module/util/Struct.java
@@ -518,7 +518,8 @@
private static FieldInfo[] getClassFieldInfo(final Class clazz) {
if (!isStructSubclass(clazz)) {
throw new IllegalArgumentException(clazz.getName() + " is not a subclass of "
- + Struct.class.getName());
+ + Struct.class.getName() + ", its superclass is "
+ + clazz.getSuperclass().getName());
}
final FieldInfo[] cachedAnnotationFields = sFieldCache.get(clazz);
@@ -730,4 +731,32 @@
}
return sb.toString();
}
+
+ /** A simple Struct which only contains a u8 field. */
+ public static class U8 extends Struct {
+ @Struct.Field(order = 0, type = Struct.Type.U8)
+ public final short val;
+
+ public U8(final short val) {
+ this.val = val;
+ }
+ }
+
+ public static class U32 extends Struct {
+ @Struct.Field(order = 0, type = Struct.Type.U32)
+ public final long val;
+
+ public U32(final long val) {
+ this.val = val;
+ }
+ }
+
+ public static class S64 extends Struct {
+ @Struct.Field(order = 0, type = Struct.Type.S64)
+ public final long val;
+
+ public S64(final long val) {
+ this.val = val;
+ }
+ }
}
diff --git a/common/device/com/android/net/module/util/TcUtils.java b/common/device/com/android/net/module/util/TcUtils.java
new file mode 100644
index 0000000..cf01490
--- /dev/null
+++ b/common/device/com/android/net/module/util/TcUtils.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util;
+
+import java.io.IOException;
+
+/**
+ * Contains mostly tc-related functionality.
+ */
+public class TcUtils {
+ static {
+ System.loadLibrary(JniUtil.getJniLibraryName(TcUtils.class.getPackage()));
+ }
+
+ /**
+ * Checks if the network interface uses an ethernet L2 header.
+ *
+ * @param iface the network interface.
+ * @return true if the interface uses an ethernet L2 header.
+ * @throws IOException
+ */
+ public static native boolean isEthernet(String iface) throws IOException;
+
+ /**
+ * Attach a tc bpf filter.
+ *
+ * Equivalent to the following 'tc' command:
+ * tc filter add dev .. in/egress prio .. protocol ipv6/ip bpf object-pinned
+ * /sys/fs/bpf/... direct-action
+ *
+ * @param ifIndex the network interface index.
+ * @param ingress ingress or egress qdisc.
+ * @param prio
+ * @param proto
+ * @param bpfProgPath
+ * @throws IOException
+ */
+ public static native void tcFilterAddDevBpf(int ifIndex, boolean ingress, short prio,
+ short proto, String bpfProgPath) throws IOException;
+
+ /**
+ * Attach a tc police action.
+ *
+ * Attaches a matchall filter to the clsact qdisc with a tc police and tc bpf action attached.
+ * This causes the ingress rate to be limited and exceeding packets to be forwarded to a bpf
+ * program (specified in bpfProgPah) that accounts for the packets before dropping them.
+ *
+ * Equivalent to the following 'tc' command:
+ * tc filter add dev .. ingress prio .. protocol .. matchall \
+ * action police rate .. burst .. conform-exceed pipe/continue \
+ * action bpf object-pinned .. \
+ * drop
+ *
+ * @param ifIndex the network interface index.
+ * @param prio the filter preference.
+ * @param proto protocol.
+ * @param rateInBytesPerSec rate limit in bytes/s.
+ * @param bpfProgPath bpg program that accounts for rate exceeding packets before they are
+ * dropped.
+ * @throws IOException
+ */
+ public static native void tcFilterAddDevIngressPolice(int ifIndex, short prio, short proto,
+ int rateInBytesPerSec, String bpfProgPath) throws IOException;
+
+ /**
+ * Delete a tc filter.
+ *
+ * Equivalent to the following 'tc' command:
+ * tc filter del dev .. in/egress prio .. protocol ..
+ *
+ * @param ifIndex the network interface index.
+ * @param ingress ingress or egress qdisc.
+ * @param prio the filter preference.
+ * @param proto protocol.
+ * @throws IOException
+ */
+ public static native void tcFilterDelDev(int ifIndex, boolean ingress, short prio,
+ short proto) throws IOException;
+}
diff --git a/common/framework/com/android/net/module/util/NetworkCapabilitiesUtils.java b/common/framework/com/android/net/module/util/NetworkCapabilitiesUtils.java
index d6222a7..26c24f8 100644
--- a/common/framework/com/android/net/module/util/NetworkCapabilitiesUtils.java
+++ b/common/framework/com/android/net/module/util/NetworkCapabilitiesUtils.java
@@ -27,6 +27,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_MCX;
import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_MMTEL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE;
import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS;
@@ -84,40 +85,41 @@
* and {@code FORCE_RESTRICTED_CAPABILITIES}.
*/
@VisibleForTesting
- static final long RESTRICTED_CAPABILITIES =
- (1 << NET_CAPABILITY_BIP)
- | (1 << NET_CAPABILITY_CBS)
- | (1 << NET_CAPABILITY_DUN)
- | (1 << NET_CAPABILITY_EIMS)
- | (1 << NET_CAPABILITY_ENTERPRISE)
- | (1 << NET_CAPABILITY_FOTA)
- | (1 << NET_CAPABILITY_IA)
- | (1 << NET_CAPABILITY_IMS)
- | (1 << NET_CAPABILITY_MCX)
- | (1 << NET_CAPABILITY_RCS)
- | (1 << NET_CAPABILITY_VEHICLE_INTERNAL)
- | (1 << NET_CAPABILITY_VSIM)
- | (1 << NET_CAPABILITY_XCAP);
+ public static final long RESTRICTED_CAPABILITIES = packBitList(
+ NET_CAPABILITY_BIP,
+ NET_CAPABILITY_CBS,
+ NET_CAPABILITY_DUN,
+ NET_CAPABILITY_EIMS,
+ NET_CAPABILITY_ENTERPRISE,
+ NET_CAPABILITY_FOTA,
+ NET_CAPABILITY_IA,
+ NET_CAPABILITY_IMS,
+ NET_CAPABILITY_MCX,
+ NET_CAPABILITY_RCS,
+ NET_CAPABILITY_VEHICLE_INTERNAL,
+ NET_CAPABILITY_VSIM,
+ NET_CAPABILITY_XCAP,
+ NET_CAPABILITY_MMTEL);
/**
* Capabilities that force network to be restricted.
* See {@code NetworkCapabilities#maybeMarkCapabilitiesRestricted}.
*/
- private static final long FORCE_RESTRICTED_CAPABILITIES =
- (1 << NET_CAPABILITY_ENTERPRISE)
- | (1 << NET_CAPABILITY_OEM_PAID)
- | (1 << NET_CAPABILITY_OEM_PRIVATE);
+ private static final long FORCE_RESTRICTED_CAPABILITIES = packBitList(
+ NET_CAPABILITY_ENTERPRISE,
+ NET_CAPABILITY_OEM_PAID,
+ NET_CAPABILITY_OEM_PRIVATE);
/**
* Capabilities that suggest that a network is unrestricted.
* See {@code NetworkCapabilities#maybeMarkCapabilitiesRestricted}.
*/
@VisibleForTesting
- static final long UNRESTRICTED_CAPABILITIES =
- (1 << NET_CAPABILITY_INTERNET)
- | (1 << NET_CAPABILITY_MMS)
- | (1 << NET_CAPABILITY_SUPL)
- | (1 << NET_CAPABILITY_WIFI_P2P);
+ public static final long UNRESTRICTED_CAPABILITIES = packBitList(
+ NET_CAPABILITY_INTERNET,
+ NET_CAPABILITY_MMS,
+ NET_CAPABILITY_SUPL,
+ NET_CAPABILITY_WIFI_P2P);
/**
* Get a transport that can be used to classify a network when displaying its info to users.
@@ -196,7 +198,26 @@
}
/**
+ * Packs a list of ints in the same way as packBits()
+ *
+ * Each passed int is the rank of a bit that should be set in the returned long.
+ * Example : passing (1,3) will return in 0b00001010 and passing (5,6,0) will return 0b01100001
+ *
+ * @param bits bits to pack
+ * @return a long with the specified bits set.
+ */
+ public static long packBitList(int... bits) {
+ return packBits(bits);
+ }
+
+ /**
* Packs array of bits into a long value.
+ *
+ * Each passed int is the rank of a bit that should be set in the returned long.
+ * Example : passing [1,3] will return in 0b00001010 and passing [5,6,0] will return 0b01100001
+ *
+ * @param bits bits to pack
+ * @return a long with the specified bits set.
*/
public static long packBits(int[] bits) {
long packed = 0;
diff --git a/common/framework/com/android/net/module/util/NetworkStatsUtils.java b/common/framework/com/android/net/module/util/NetworkStatsUtils.java
index d4ad1f2..41a9428 100644
--- a/common/framework/com/android/net/module/util/NetworkStatsUtils.java
+++ b/common/framework/com/android/net/module/util/NetworkStatsUtils.java
@@ -16,12 +16,23 @@
package com.android.net.module.util;
+import android.app.usage.NetworkStats;
+
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* Various utilities used for NetworkStats related code.
*
* @hide
*/
public class NetworkStatsUtils {
+ // These constants must be synced with the definition in android.net.NetworkStats.
+ // TODO: update to formal APIs once all downstreams have these APIs.
+ private static final int SET_ALL = -1;
+ private static final int METERED_ALL = -1;
+ private static final int ROAMING_ALL = -1;
+ private static final int DEFAULT_NETWORK_ALL = -1;
+
/**
* Safely multiple a value by a rational.
* <p>
@@ -68,6 +79,12 @@
public static final int SUBSCRIBER_ID_MATCH_RULE_ALL = 1;
/**
+ * Name representing {@link #bandwidthSetGlobalAlert(long)} limit when delivered to
+ * {@link AlertObserver#onQuotaLimitReached(String, String)}.
+ */
+ public static final String LIMIT_GLOBAL_ALERT = "globalAlert";
+
+ /**
* Return the constrained value by given the lower and upper bounds.
*/
public static int constrain(int amount, int low, int high) {
@@ -82,4 +99,77 @@
if (low > high) throw new IllegalArgumentException("low(" + low + ") > high(" + high + ")");
return amount < low ? low : (amount > high ? high : amount);
}
+
+ /**
+ * Convert structure from android.app.usage.NetworkStats to android.net.NetworkStats.
+ */
+ public static android.net.NetworkStats fromPublicNetworkStats(
+ NetworkStats publiceNetworkStats) {
+ android.net.NetworkStats stats = new android.net.NetworkStats(0L, 0);
+ while (publiceNetworkStats.hasNextBucket()) {
+ NetworkStats.Bucket bucket = new NetworkStats.Bucket();
+ publiceNetworkStats.getNextBucket(bucket);
+ final android.net.NetworkStats.Entry entry = fromBucket(bucket);
+ stats = stats.addEntry(entry);
+ }
+ return stats;
+ }
+
+ @VisibleForTesting
+ public static android.net.NetworkStats.Entry fromBucket(NetworkStats.Bucket bucket) {
+ return new android.net.NetworkStats.Entry(
+ null /* IFACE_ALL */, bucket.getUid(), convertBucketState(bucket.getState()),
+ convertBucketTag(bucket.getTag()), convertBucketMetered(bucket.getMetered()),
+ convertBucketRoaming(bucket.getRoaming()),
+ convertBucketDefaultNetworkStatus(bucket.getDefaultNetworkStatus()),
+ bucket.getRxBytes(), bucket.getRxPackets(),
+ bucket.getTxBytes(), bucket.getTxPackets(), 0 /* operations */);
+ }
+
+ private static int convertBucketState(int networkStatsSet) {
+ switch (networkStatsSet) {
+ case NetworkStats.Bucket.STATE_ALL: return SET_ALL;
+ case NetworkStats.Bucket.STATE_DEFAULT: return android.net.NetworkStats.SET_DEFAULT;
+ case NetworkStats.Bucket.STATE_FOREGROUND:
+ return android.net.NetworkStats.SET_FOREGROUND;
+ }
+ return 0;
+ }
+
+ private static int convertBucketTag(int tag) {
+ switch (tag) {
+ case NetworkStats.Bucket.TAG_NONE: return android.net.NetworkStats.TAG_NONE;
+ }
+ return tag;
+ }
+
+ private static int convertBucketMetered(int metered) {
+ switch (metered) {
+ case NetworkStats.Bucket.METERED_ALL: return METERED_ALL;
+ case NetworkStats.Bucket.METERED_NO: return android.net.NetworkStats.METERED_NO;
+ case NetworkStats.Bucket.METERED_YES: return android.net.NetworkStats.METERED_YES;
+ }
+ return 0;
+ }
+
+ private static int convertBucketRoaming(int roaming) {
+ switch (roaming) {
+ case NetworkStats.Bucket.ROAMING_ALL: return ROAMING_ALL;
+ case NetworkStats.Bucket.ROAMING_NO: return android.net.NetworkStats.ROAMING_NO;
+ case NetworkStats.Bucket.ROAMING_YES: return android.net.NetworkStats.ROAMING_YES;
+ }
+ return 0;
+ }
+
+ private static int convertBucketDefaultNetworkStatus(int defaultNetworkStatus) {
+ switch (defaultNetworkStatus) {
+ case NetworkStats.Bucket.DEFAULT_NETWORK_ALL:
+ return DEFAULT_NETWORK_ALL;
+ case NetworkStats.Bucket.DEFAULT_NETWORK_NO:
+ return android.net.NetworkStats.DEFAULT_NETWORK_NO;
+ case NetworkStats.Bucket.DEFAULT_NETWORK_YES:
+ return android.net.NetworkStats.DEFAULT_NETWORK_YES;
+ }
+ return 0;
+ }
}
diff --git a/common/native/bpf_headers/Android.bp b/common/native/bpf_headers/Android.bp
index 06ba1b0..ebb5678 100644
--- a/common/native/bpf_headers/Android.bp
+++ b/common/native/bpf_headers/Android.bp
@@ -37,6 +37,7 @@
],
visibility: [
"//bootable/libbootloader/vts",
+ "//cts/tests/tests/net/native",
"//frameworks/base/services/core/jni",
"//frameworks/native/libs/cputimeinstate",
"//frameworks/native/services/gpuservice",
@@ -45,12 +46,13 @@
"//frameworks/native/services/gpuservice/tracing",
"//packages/modules/Connectivity/bpf_progs",
"//packages/modules/Connectivity/netd",
+ "//packages/modules/Connectivity/service/native",
+ "//packages/modules/Connectivity/service/native/libs/libclat",
"//packages/modules/Connectivity/tests/unit/jni",
"//packages/modules/DnsResolver/tests",
"//system/bpf/bpfloader",
"//system/bpf/libbpf_android",
"//system/memory/libmeminfo",
- "//system/netd/libnetdbpf",
"//system/netd/server",
"//system/netd/tests",
"//system/netd/tests/benchmarks",
diff --git a/common/native/bpf_headers/include/bpf/bpf_helpers.h b/common/native/bpf_headers/include/bpf/bpf_helpers.h
index 878bb10..ac9f9bc 100644
--- a/common/native/bpf_headers/include/bpf/bpf_helpers.h
+++ b/common/native/bpf_headers/include/bpf/bpf_helpers.h
@@ -116,6 +116,15 @@
static int (*bpf_map_delete_elem_unsafe)(const struct bpf_map_def* map,
const void* key) = (void*)BPF_FUNC_map_delete_elem;
+#define BPF_ANNOTATE_KV_PAIR(name, type_key, type_val) \
+ struct ____btf_map_##name { \
+ type_key key; \
+ type_val value; \
+ }; \
+ struct ____btf_map_##name \
+ __attribute__ ((section(".maps." #name), used)) \
+ ____btf_map_##name = { }
+
/* type safe macro to declare a map and related accessor functions */
#define DEFINE_BPF_MAP_UGM(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, usr, grp, md) \
const struct bpf_map_def SECTION("maps") the_map = { \
@@ -132,6 +141,7 @@
.min_kver = KVER_NONE, \
.max_kver = KVER_INF, \
}; \
+ BPF_ANNOTATE_KV_PAIR(the_map, TypeOfKey, TypeOfValue); \
\
static inline __always_inline __unused TypeOfValue* bpf_##the_map##_lookup_elem( \
const TypeOfKey* k) { \
diff --git a/common/native/bpf_syscall_wrappers/Android.bp b/common/native/bpf_syscall_wrappers/Android.bp
index 1a1b53c..26f311b 100644
--- a/common/native/bpf_syscall_wrappers/Android.bp
+++ b/common/native/bpf_syscall_wrappers/Android.bp
@@ -36,8 +36,11 @@
visibility: [
"//frameworks/libs/net/common/native/bpf_headers",
"//frameworks/libs/net/common/native/bpfmapjni",
+ "//frameworks/libs/net/common/native/tcutils",
"//packages/modules/Connectivity/netd",
"//packages/modules/Connectivity/service",
+ "//packages/modules/Connectivity/service/native",
+ "//packages/modules/Connectivity/service/native/libs/libclat",
"//packages/modules/Connectivity/Tethering",
"//packages/providers/MediaProvider/jni",
"//system/bpf/libbpf_android",
diff --git a/common/native/bpfmapjni/Android.bp b/common/native/bpfmapjni/Android.bp
index b7af22d..cd254d4 100644
--- a/common/native/bpfmapjni/Android.bp
+++ b/common/native/bpfmapjni/Android.bp
@@ -18,7 +18,10 @@
cc_library_static {
name: "libnet_utils_device_common_bpfjni",
- srcs: ["com_android_net_module_util_BpfMap.cpp"],
+ srcs: [
+ "com_android_net_module_util_BpfMap.cpp",
+ "com_android_net_module_util_TcUtils.cpp",
+ ],
header_libs: [
"bpf_syscall_wrappers",
"jni_headers",
@@ -27,6 +30,9 @@
"liblog",
"libnativehelper_compat_libc++",
],
+ whole_static_libs: [
+ "libtcutils",
+ ],
cflags: [
"-Wall",
"-Werror",
@@ -40,5 +46,7 @@
],
visibility: [
"//packages/modules/Connectivity:__subpackages__",
+ // TODO: remove after NetworkStatsService moves to the module.
+ "//frameworks/base/packages/ConnectivityT/service",
],
}
diff --git a/common/native/bpfmapjni/com_android_net_module_util_TcUtils.cpp b/common/native/bpfmapjni/com_android_net_module_util_TcUtils.cpp
new file mode 100644
index 0000000..2307a6b
--- /dev/null
+++ b/common/native/bpfmapjni/com_android_net_module_util_TcUtils.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/scoped_utf_chars.h>
+#include <tcutils/tcutils.h>
+
+namespace android {
+
+static void throwIOException(JNIEnv *env, const char *msg, int error) {
+ jniThrowExceptionFmt(env, "java/io/IOException", "%s: %s", msg,
+ strerror(error));
+}
+
+static jboolean com_android_net_module_util_TcUtils_isEthernet(JNIEnv *env,
+ jobject clazz,
+ jstring iface) {
+ ScopedUtfChars interface(env, iface);
+ bool result = false;
+ int error = isEthernet(interface.c_str(), result);
+ if (error) {
+ throwIOException(
+ env, "com_android_net_module_util_TcUtils_isEthernet error: ", error);
+ }
+ // result is not touched when error is returned; leave false.
+ return result;
+}
+
+// tc filter add dev .. in/egress prio 1 protocol ipv6/ip bpf object-pinned
+// /sys/fs/bpf/... direct-action
+static void com_android_net_module_util_TcUtils_tcFilterAddDevBpf(
+ JNIEnv *env, jobject clazz, jint ifIndex, jboolean ingress, jshort prio,
+ jshort proto, jstring bpfProgPath) {
+ ScopedUtfChars pathname(env, bpfProgPath);
+ int error = tcAddBpfFilter(ifIndex, ingress, prio, proto, pathname.c_str());
+ if (error) {
+ throwIOException(
+ env,
+ "com_android_net_module_util_TcUtils_tcFilterAddDevBpf error: ", error);
+ }
+}
+
+// tc filter add dev .. ingress prio .. protocol .. matchall \
+// action police rate .. burst .. conform-exceed pipe/continue \
+// action bpf object-pinned .. \
+// drop
+static void com_android_net_module_util_TcUtils_tcFilterAddDevIngressPolice(
+ JNIEnv *env, jobject clazz, jint ifIndex, jshort prio, jshort proto,
+ jint rateInBytesPerSec, jstring bpfProgPath) {
+ ScopedUtfChars pathname(env, bpfProgPath);
+ int error = tcAddIngressPoliceFilter(ifIndex, prio, proto, rateInBytesPerSec,
+ pathname.c_str());
+ if (error) {
+ throwIOException(env,
+ "com_android_net_module_util_TcUtils_"
+ "tcFilterAddDevIngressPolice error: ",
+ error);
+ }
+}
+
+// tc filter del dev .. in/egress prio .. protocol ..
+static void com_android_net_module_util_TcUtils_tcFilterDelDev(
+ JNIEnv *env, jobject clazz, jint ifIndex, jboolean ingress, jshort prio,
+ jshort proto) {
+ int error = tcDeleteFilter(ifIndex, ingress, prio, proto);
+ if (error) {
+ throwIOException(
+ env,
+ "com_android_net_module_util_TcUtils_tcFilterDelDev error: ", error);
+ }
+}
+
+/*
+ * JNI registration.
+ */
+static const JNINativeMethod gMethods[] = {
+ /* name, signature, funcPtr */
+ {"isEthernet", "(Ljava/lang/String;)Z",
+ (void *)com_android_net_module_util_TcUtils_isEthernet},
+ {"tcFilterAddDevBpf", "(IZSSLjava/lang/String;)V",
+ (void *)com_android_net_module_util_TcUtils_tcFilterAddDevBpf},
+ {"tcFilterAddDevIngressPolice", "(ISSILjava/lang/String;)V",
+ (void *)com_android_net_module_util_TcUtils_tcFilterAddDevIngressPolice},
+ {"tcFilterDelDev", "(IZSS)V",
+ (void *)com_android_net_module_util_TcUtils_tcFilterDelDev},
+};
+
+int register_com_android_net_module_util_TcUtils(JNIEnv *env,
+ char const *class_name) {
+ return jniRegisterNativeMethods(env, class_name, gMethods, NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/common/native/nettestutils/Android.bp b/common/native/nettestutils/Android.bp
new file mode 100644
index 0000000..42df8e0
--- /dev/null
+++ b/common/native/nettestutils/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library_static {
+ name: "libnettestutils",
+ export_include_dirs: ["include"],
+ srcs: ["DumpService.cpp"],
+
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+}
diff --git a/common/native/nettestutils/DumpService.cpp b/common/native/nettestutils/DumpService.cpp
new file mode 100644
index 0000000..ba3d77e
--- /dev/null
+++ b/common/native/nettestutils/DumpService.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "nettestutils/DumpService.h"
+
+#include <android-base/file.h>
+
+#include <sstream>
+#include <thread>
+
+android::status_t dumpService(const android::sp<android::IBinder>& binder,
+ const std::vector<std::string>& args,
+ std::vector<std::string>& outputLines) {
+ if (!outputLines.empty()) return -EUCLEAN;
+
+ android::base::unique_fd localFd, remoteFd;
+ if (!Pipe(&localFd, &remoteFd)) return -errno;
+
+ android::Vector<android::String16> str16Args;
+ for (const auto& arg : args) {
+ str16Args.push(android::String16(arg.c_str()));
+ }
+ android::status_t ret;
+ // dump() blocks until another thread has consumed all its output.
+ std::thread dumpThread =
+ std::thread([&ret, binder, remoteFd{std::move(remoteFd)}, str16Args]() {
+ ret = binder->dump(remoteFd, str16Args);
+ });
+
+ std::string dumpContent;
+ if (!android::base::ReadFdToString(localFd.get(), &dumpContent)) {
+ return -errno;
+ }
+ dumpThread.join();
+ if (ret != android::OK) return ret;
+
+ std::stringstream dumpStream(std::move(dumpContent));
+ std::string line;
+ while (std::getline(dumpStream, line)) {
+ outputLines.push_back(line);
+ }
+
+ return android::OK;
+}
diff --git a/common/native/nettestutils/include/nettestutils/DumpService.h b/common/native/nettestutils/include/nettestutils/DumpService.h
new file mode 100644
index 0000000..2a72181
--- /dev/null
+++ b/common/native/nettestutils/include/nettestutils/DumpService.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/Binder.h>
+
+#include <vector>
+
+android::status_t dumpService(const android::sp<android::IBinder>& binder,
+ const std::vector<std::string>& args,
+ std::vector<std::string>& outputLines);
diff --git a/common/native/tcutils/Android.bp b/common/native/tcutils/Android.bp
new file mode 100644
index 0000000..e819e4c
--- /dev/null
+++ b/common/native/tcutils/Android.bp
@@ -0,0 +1,68 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library_static {
+ name: "libtcutils",
+ srcs: ["tcutils.cpp"],
+ export_include_dirs: ["include"],
+ header_libs: ["bpf_syscall_wrappers"],
+ shared_libs: [
+ "liblog",
+ ],
+ stl: "libc++_static",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ ],
+ sdk_version: "30",
+ min_sdk_version: "30",
+ apex_available: [
+ "com.android.tethering",
+ "//apex_available:platform",
+ ],
+ visibility: [
+ "//frameworks/libs/net/common/native/bpfmapjni",
+ "//packages/modules/Connectivity:__subpackages__",
+ "//system/netd/server",
+ ],
+}
+
+cc_test {
+ name: "libtcutils_test",
+ srcs: [
+ "tests/tcutils_test.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-error=unused-variable",
+ ],
+ header_libs: ["bpf_syscall_wrappers"],
+ static_libs: [
+ "libgmock",
+ "libtcutils",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+ min_sdk_version: "30",
+ require_root: true,
+ test_suites: ["general-tests"],
+}
diff --git a/common/native/tcutils/include/tcutils/tcutils.h b/common/native/tcutils/include/tcutils/tcutils.h
new file mode 100644
index 0000000..a8ec2e8
--- /dev/null
+++ b/common/native/tcutils/include/tcutils/tcutils.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <linux/rtnetlink.h>
+
+namespace android {
+
+int isEthernet(const char *iface, bool &isEthernet);
+
+int doTcQdiscClsact(int ifIndex, uint16_t nlMsgType, uint16_t nlMsgFlags);
+
+static inline int tcAddQdiscClsact(int ifIndex) {
+ return doTcQdiscClsact(ifIndex, RTM_NEWQDISC, NLM_F_EXCL | NLM_F_CREATE);
+}
+
+static inline int tcReplaceQdiscClsact(int ifIndex) {
+ return doTcQdiscClsact(ifIndex, RTM_NEWQDISC, NLM_F_CREATE | NLM_F_REPLACE);
+}
+
+static inline int tcDeleteQdiscClsact(int ifIndex) {
+ return doTcQdiscClsact(ifIndex, RTM_DELQDISC, 0);
+}
+
+int tcAddBpfFilter(int ifIndex, bool ingress, uint16_t prio, uint16_t proto,
+ const char *bpfProgPath);
+int tcAddIngressPoliceFilter(int ifIndex, uint16_t prio, uint16_t proto,
+ unsigned rateInBytesPerSec,
+ const char *bpfProgPath);
+int tcDeleteFilter(int ifIndex, bool ingress, uint16_t prio, uint16_t proto);
+
+} // namespace android
diff --git a/common/native/tcutils/kernelversion.h b/common/native/tcutils/kernelversion.h
new file mode 100644
index 0000000..3be1ad2
--- /dev/null
+++ b/common/native/tcutils/kernelversion.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// -----------------------------------------------------------------------------
+// TODO - This should be replaced with BpfUtils in bpf_headers.
+// Currently, bpf_headers contains a bunch requirements it doesn't actually provide, such as a
+// non-ndk liblog version, and some version of libbase. libtcutils does not have access to either of
+// these, so I think this will have to wait until we figure out a way around this.
+//
+// In the mean time copying verbatim from:
+// frameworks/libs/net/common/native/bpf_headers
+
+#pragma once
+
+#include <stdio.h>
+#include <sys/utsname.h>
+
+#define KVER(a, b, c) (((a) << 24) + ((b) << 16) + (c))
+
+namespace android {
+
+static inline unsigned kernelVersion() {
+ struct utsname buf;
+ int ret = uname(&buf);
+ if (ret)
+ return 0;
+
+ unsigned kver_major;
+ unsigned kver_minor;
+ unsigned kver_sub;
+ char discard;
+ ret = sscanf(buf.release, "%u.%u.%u%c", &kver_major, &kver_minor, &kver_sub,
+ &discard);
+ // Check the device kernel version
+ if (ret < 3)
+ return 0;
+
+ return KVER(kver_major, kver_minor, kver_sub);
+}
+
+static inline bool isAtLeastKernelVersion(unsigned major, unsigned minor,
+ unsigned sub) {
+ return kernelVersion() >= KVER(major, minor, sub);
+}
+
+} // namespace android
diff --git a/common/native/tcutils/logging.h b/common/native/tcutils/logging.h
new file mode 100644
index 0000000..70604b3
--- /dev/null
+++ b/common/native/tcutils/logging.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/log.h>
+#include <stdarg.h>
+
+#ifndef LOG_TAG
+#define LOG_TAG "TcUtils_Undef"
+#endif
+
+namespace android {
+
+static inline void ALOGE(const char *fmt...) {
+ va_list args;
+ va_start(args, fmt);
+ __android_log_vprint(ANDROID_LOG_ERROR, LOG_TAG, fmt, args);
+ va_end(args);
+}
+
+}
diff --git a/common/native/tcutils/scopeguard.h b/common/native/tcutils/scopeguard.h
new file mode 100644
index 0000000..76bbb93
--- /dev/null
+++ b/common/native/tcutils/scopeguard.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// -----------------------------------------------------------------------------
+// TODO: figure out a way to use libbase_ndk. This is currently not working
+// because of missing apex availability. For now, we can use a copy of
+// ScopeGuard which is very lean compared to unique_fd. This code has been
+// copied verbatim from:
+// https://cs.android.com/android/platform/superproject/+/master:system/libbase/include/android-base/scopeguard.h
+
+#pragma once
+
+#include <utility> // for std::move, std::forward
+
+namespace android {
+namespace base {
+
+// ScopeGuard ensures that the specified functor is executed no matter how the
+// current scope exits.
+template <typename F> class ScopeGuard {
+public:
+ ScopeGuard(F &&f) : f_(std::forward<F>(f)), active_(true) {}
+
+ ScopeGuard(ScopeGuard &&that) noexcept
+ : f_(std::move(that.f_)), active_(that.active_) {
+ that.active_ = false;
+ }
+
+ template <typename Functor>
+ ScopeGuard(ScopeGuard<Functor> &&that)
+ : f_(std::move(that.f_)), active_(that.active_) {
+ that.active_ = false;
+ }
+
+ ~ScopeGuard() {
+ if (active_)
+ f_();
+ }
+
+ ScopeGuard() = delete;
+ ScopeGuard(const ScopeGuard &) = delete;
+ void operator=(const ScopeGuard &) = delete;
+ void operator=(ScopeGuard &&that) = delete;
+
+ void Disable() { active_ = false; }
+
+ bool active() const { return active_; }
+
+private:
+ template <typename Functor> friend class ScopeGuard;
+
+ F f_;
+ bool active_;
+};
+
+template <typename F> ScopeGuard<F> make_scope_guard(F &&f) {
+ return ScopeGuard<F>(std::forward<F>(f));
+}
+
+} // namespace base
+} // namespace android
diff --git a/common/native/tcutils/tcutils.cpp b/common/native/tcutils/tcutils.cpp
new file mode 100644
index 0000000..0e17f67
--- /dev/null
+++ b/common/native/tcutils/tcutils.cpp
@@ -0,0 +1,736 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TcUtils"
+
+#include "tcutils/tcutils.h"
+
+#include "logging.h"
+#include "kernelversion.h"
+#include "scopeguard.h"
+
+#include <arpa/inet.h>
+#include <cerrno>
+#include <cstring>
+#include <libgen.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/netlink.h>
+#include <linux/pkt_cls.h>
+#include <linux/pkt_sched.h>
+#include <linux/rtnetlink.h>
+#include <linux/tc_act/tc_bpf.h>
+#include <net/if.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <utility>
+
+#define BPF_FD_JUST_USE_INT
+#include <BpfSyscallWrappers.h>
+#undef BPF_FD_JUST_USE_INT
+
+// The maximum length of TCA_BPF_NAME. Sync from net/sched/cls_bpf.c.
+#define CLS_BPF_NAME_LEN 256
+
+// Classifier name. See cls_bpf_ops in net/sched/cls_bpf.c.
+#define CLS_BPF_KIND_NAME "bpf"
+
+namespace android {
+namespace {
+
+/**
+ * IngressPoliceFilterBuilder builds a nlmsg request equivalent to the following
+ * tc command:
+ *
+ * tc filter add dev .. ingress prio .. protocol .. matchall \
+ * action police rate .. burst .. conform-exceed pipe/continue \
+ * action bpf object-pinned .. \
+ * drop
+ */
+class IngressPoliceFilterBuilder final {
+ // default mtu is 2047, so the cell logarithm factor (cell_log) is 3.
+ // 0x7FF >> 0x3FF x 2^1 >> 0x1FF x 2^2 >> 0xFF x 2^3
+ static constexpr int RTAB_CELL_LOGARITHM = 3;
+ static constexpr size_t RTAB_SIZE = 256;
+ static constexpr unsigned TIME_UNITS_PER_SEC = 1000000;
+
+ struct Request {
+ nlmsghdr n;
+ tcmsg t;
+ struct {
+ nlattr attr;
+ char str[NLMSG_ALIGN(sizeof("matchall"))];
+ } kind;
+ struct {
+ nlattr attr;
+ struct {
+ nlattr attr;
+ struct {
+ nlattr attr;
+ struct {
+ nlattr attr;
+ char str[NLMSG_ALIGN(sizeof("police"))];
+ } kind;
+ struct {
+ nlattr attr;
+ struct {
+ nlattr attr;
+ struct tc_police obj;
+ } police;
+ struct {
+ nlattr attr;
+ uint32_t u32[RTAB_SIZE];
+ } rtab;
+ struct {
+ nlattr attr;
+ int32_t s32;
+ } notexceedact;
+ } opt;
+ } act1;
+ struct {
+ nlattr attr;
+ struct {
+ nlattr attr;
+ char str[NLMSG_ALIGN(sizeof("bpf"))];
+ } kind;
+ struct {
+ nlattr attr;
+ struct {
+ nlattr attr;
+ uint32_t u32;
+ } fd;
+ struct {
+ nlattr attr;
+ char str[NLMSG_ALIGN(CLS_BPF_NAME_LEN)];
+ } name;
+ struct {
+ nlattr attr;
+ struct tc_act_bpf obj;
+ } parms;
+ } opt;
+ } act2;
+ } acts;
+ } opt;
+ };
+
+ // class members
+ const unsigned mBurstInBytes;
+ const char *mBpfProgPath;
+ int mBpfFd;
+ Request mRequest;
+
+ static double getTickInUsec() {
+ FILE *fp = fopen("/proc/net/psched", "re");
+ if (!fp) {
+ ALOGE("fopen(\"/proc/net/psched\"): %s", strerror(errno));
+ return 0.0;
+ }
+ auto scopeGuard = base::make_scope_guard([fp] { fclose(fp); });
+
+ uint32_t t2us;
+ uint32_t us2t;
+ uint32_t clockRes;
+ const bool isError =
+ fscanf(fp, "%08x%08x%08x", &t2us, &us2t, &clockRes) != 3;
+
+ if (isError) {
+ ALOGE("fscanf(/proc/net/psched, \"%%08x%%08x%%08x\"): %s",
+ strerror(errno));
+ return 0.0;
+ }
+
+ const double clockFactor =
+ static_cast<double>(clockRes) / TIME_UNITS_PER_SEC;
+ return static_cast<double>(t2us) / static_cast<double>(us2t) * clockFactor;
+ }
+
+ static inline const double kTickInUsec = getTickInUsec();
+
+public:
+ // clang-format off
+ IngressPoliceFilterBuilder(int ifIndex, uint16_t prio, uint16_t proto, unsigned rateInBytesPerSec,
+ unsigned burstInBytes, const char* bpfProgPath)
+ : mBurstInBytes(burstInBytes),
+ mBpfProgPath(bpfProgPath),
+ mBpfFd(-1),
+ mRequest{
+ .n = {
+ .nlmsg_len = sizeof(mRequest),
+ .nlmsg_type = RTM_NEWTFILTER,
+ .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE,
+ },
+ .t = {
+ .tcm_family = AF_UNSPEC,
+ .tcm_ifindex = ifIndex,
+ .tcm_handle = TC_H_UNSPEC,
+ .tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS),
+ .tcm_info = (static_cast<uint32_t>(prio) << 16)
+ | static_cast<uint32_t>(htons(proto)),
+ },
+ .kind = {
+ .attr = {
+ .nla_len = sizeof(mRequest.kind),
+ .nla_type = TCA_KIND,
+ },
+ .str = "matchall",
+ },
+ .opt = {
+ .attr = {
+ .nla_len = sizeof(mRequest.opt),
+ .nla_type = TCA_OPTIONS,
+ },
+ .acts = {
+ .attr = {
+ .nla_len = sizeof(mRequest.opt.acts),
+ .nla_type = TCA_U32_ACT,
+ },
+ .act1 = {
+ .attr = {
+ .nla_len = sizeof(mRequest.opt.acts.act1),
+ .nla_type = 1, // action priority
+ },
+ .kind = {
+ .attr = {
+ .nla_len = sizeof(mRequest.opt.acts.act1.kind),
+ .nla_type = TCA_ACT_KIND,
+ },
+ .str = "police",
+ },
+ .opt = {
+ .attr = {
+ .nla_len = sizeof(mRequest.opt.acts.act1.opt),
+ .nla_type = TCA_ACT_OPTIONS | NLA_F_NESTED,
+ },
+ .police = {
+ .attr = {
+ .nla_len = sizeof(mRequest.opt.acts.act1.opt.police),
+ .nla_type = TCA_POLICE_TBF,
+ },
+ .obj = {
+ .action = TC_ACT_PIPE,
+ .burst = 0,
+ .rate = {
+ .cell_log = RTAB_CELL_LOGARITHM,
+ .linklayer = TC_LINKLAYER_ETHERNET,
+ .cell_align = -1,
+ .rate = rateInBytesPerSec,
+ },
+ },
+ },
+ .rtab = {
+ .attr = {
+ .nla_len = sizeof(mRequest.opt.acts.act1.opt.rtab),
+ .nla_type = TCA_POLICE_RATE,
+ },
+ .u32 = {},
+ },
+ .notexceedact = {
+ .attr = {
+ .nla_len = sizeof(mRequest.opt.acts.act1.opt.notexceedact),
+ .nla_type = TCA_POLICE_RESULT,
+ },
+ .s32 = TC_ACT_UNSPEC,
+ },
+ },
+ },
+ .act2 = {
+ .attr = {
+ .nla_len = sizeof(mRequest.opt.acts.act2),
+ .nla_type = 2, // action priority
+ },
+ .kind = {
+ .attr = {
+ .nla_len = sizeof(mRequest.opt.acts.act2.kind),
+ .nla_type = TCA_ACT_KIND,
+ },
+ .str = "bpf",
+ },
+ .opt = {
+ .attr = {
+ .nla_len = sizeof(mRequest.opt.acts.act2.opt),
+ .nla_type = TCA_ACT_OPTIONS | NLA_F_NESTED,
+ },
+ .fd = {
+ .attr = {
+ .nla_len = sizeof(mRequest.opt.acts.act2.opt.fd),
+ .nla_type = TCA_ACT_BPF_FD,
+ },
+ .u32 = 0, // set during build()
+ },
+ .name = {
+ .attr = {
+ .nla_len = sizeof(mRequest.opt.acts.act2.opt.name),
+ .nla_type = TCA_ACT_BPF_NAME,
+ },
+ .str = "placeholder",
+ },
+ .parms = {
+ .attr = {
+ .nla_len = sizeof(mRequest.opt.acts.act2.opt.parms),
+ .nla_type = TCA_ACT_BPF_PARMS,
+ },
+ .obj = {
+ // default action to be executed when bpf prog
+ // returns TC_ACT_UNSPEC.
+ .action = TC_ACT_SHOT,
+ },
+ },
+ },
+ },
+ },
+ },
+ } {
+ // constructor body
+ }
+ // clang-format on
+
+ ~IngressPoliceFilterBuilder() {
+ // TODO: use unique_fd
+ if (mBpfFd != -1) {
+ close(mBpfFd);
+ }
+ }
+
+ constexpr unsigned getRequestSize() const { return sizeof(Request); }
+
+private:
+ unsigned calculateXmitTime(unsigned size) {
+ const uint32_t rate = mRequest.opt.acts.act1.opt.police.obj.rate.rate;
+ return (static_cast<double>(size) / static_cast<double>(rate)) *
+ TIME_UNITS_PER_SEC * kTickInUsec;
+ }
+
+ void initBurstRate() {
+ mRequest.opt.acts.act1.opt.police.obj.burst =
+ calculateXmitTime(mBurstInBytes);
+ }
+
+ // Calculates a table with 256 transmission times for different packet sizes
+ // (all the way up to MTU). RTAB_CELL_LOGARITHM is used as a scaling factor.
+ // In this case, MTU size is always 2048, so RTAB_CELL_LOGARITHM is always
+ // 3. Therefore, this function generates the transmission times for packets
+ // of size 1..256 x 2^3.
+ void initRateTable() {
+ for (unsigned i = 0; i < RTAB_SIZE; ++i) {
+ unsigned adjustedSize = (i + 1) << RTAB_CELL_LOGARITHM;
+ mRequest.opt.acts.act1.opt.rtab.u32[i] = calculateXmitTime(adjustedSize);
+ }
+ }
+
+ int initBpfFd() {
+ mBpfFd = bpf::retrieveProgram(mBpfProgPath);
+ if (mBpfFd == -1) {
+ int error = errno;
+ ALOGE("retrieveProgram failed: %d", error);
+ return -error;
+ }
+
+ mRequest.opt.acts.act2.opt.fd.u32 = static_cast<uint32_t>(mBpfFd);
+ snprintf(mRequest.opt.acts.act2.opt.name.str,
+ sizeof(mRequest.opt.acts.act2.opt.name.str), "%s:[*fsobj]",
+ basename(mBpfProgPath));
+
+ return 0;
+ }
+
+public:
+ int build() {
+ if (kTickInUsec == 0.0) {
+ return -EINVAL;
+ }
+
+ initBurstRate();
+ initRateTable();
+ return initBpfFd();
+ }
+
+ const Request *getRequest() const {
+ // Make sure to call build() before calling this function. Otherwise, the
+ // request will be invalid.
+ return &mRequest;
+ }
+};
+
+const sockaddr_nl KERNEL_NLADDR = {AF_NETLINK, 0, 0, 0};
+const uint16_t NETLINK_REQUEST_FLAGS = NLM_F_REQUEST | NLM_F_ACK;
+
+int sendAndProcessNetlinkResponse(const void *req, int len) {
+ // TODO: use unique_fd instead of ScopeGuard
+ int fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
+ if (fd == -1) {
+ int error = errno;
+ ALOGE("socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE): %d",
+ error);
+ return -error;
+ }
+ auto scopeGuard = base::make_scope_guard([fd] { close(fd); });
+
+ static constexpr int on = 1;
+ if (setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, &on, sizeof(on))) {
+ int error = errno;
+ ALOGE("setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, 1): %d", error);
+ return -error;
+ }
+
+ // this is needed to get valid strace netlink parsing, it allocates the pid
+ if (bind(fd, (const struct sockaddr *)&KERNEL_NLADDR,
+ sizeof(KERNEL_NLADDR))) {
+ int error = errno;
+ ALOGE("bind(fd, {AF_NETLINK, 0, 0}: %d)", error);
+ return -error;
+ }
+
+ // we do not want to receive messages from anyone besides the kernel
+ if (connect(fd, (const struct sockaddr *)&KERNEL_NLADDR,
+ sizeof(KERNEL_NLADDR))) {
+ int error = errno;
+ ALOGE("connect(fd, {AF_NETLINK, 0, 0}): %d", error);
+ return -error;
+ }
+
+ int rv = send(fd, req, len, 0);
+
+ if (rv == -1) {
+ int error = errno;
+ ALOGE("send(fd, req, len, 0) failed: %d", error);
+ return -error;
+ }
+
+ if (rv != len) {
+ ALOGE("send(fd, req, len = %d, 0) returned invalid message size %d", len,
+ rv);
+ return -EMSGSIZE;
+ }
+
+ struct {
+ nlmsghdr h;
+ nlmsgerr e;
+ char buf[256];
+ } resp = {};
+
+ rv = recv(fd, &resp, sizeof(resp), MSG_TRUNC);
+
+ if (rv == -1) {
+ int error = errno;
+ ALOGE("recv() failed: %d", error);
+ return -error;
+ }
+
+ if (rv < (int)NLMSG_SPACE(sizeof(struct nlmsgerr))) {
+ ALOGE("recv() returned short packet: %d", rv);
+ return -EBADMSG;
+ }
+
+ if (resp.h.nlmsg_len != (unsigned)rv) {
+ ALOGE("recv() returned invalid header length: %d != %d",
+ resp.h.nlmsg_len, rv);
+ return -EBADMSG;
+ }
+
+ if (resp.h.nlmsg_type != NLMSG_ERROR) {
+ ALOGE("recv() did not return NLMSG_ERROR message: %d",
+ resp.h.nlmsg_type);
+ return -ENOMSG;
+ }
+
+ if (resp.e.error) {
+ ALOGE("NLMSG_ERROR message return error: %d", resp.e.error);
+ }
+ return resp.e.error; // returns 0 on success
+}
+
+int hardwareAddressType(const char *interface) {
+ int fd = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return -errno;
+ auto scopeGuard = base::make_scope_guard([fd] { close(fd); });
+
+ struct ifreq ifr = {};
+ // We use strncpy() instead of strlcpy() since kernel has to be able
+ // to handle non-zero terminated junk passed in by userspace anyway,
+ // and this way too long interface names (more than IFNAMSIZ-1 = 15
+ // characters plus terminating NULL) will not get truncated to 15
+ // characters and zero-terminated and thus potentially erroneously
+ // match a truncated interface if one were to exist.
+ strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
+
+ if (ioctl(fd, SIOCGIFHWADDR, &ifr, sizeof(ifr))) {
+ return -errno;
+ }
+ return ifr.ifr_hwaddr.sa_family;
+}
+
+} // namespace
+
+int isEthernet(const char *iface, bool &isEthernet) {
+ int rv = hardwareAddressType(iface);
+ if (rv < 0) {
+ ALOGE("Get hardware address type of interface %s failed: %s", iface,
+ strerror(-rv));
+ return rv;
+ }
+
+ // Backwards compatibility with pre-GKI kernels that use various custom
+ // ARPHRD_* for their cellular interface
+ switch (rv) {
+ // ARPHRD_PUREIP on at least some Mediatek Android kernels
+ // example: wembley with 4.19 kernel
+ case 520:
+ // in Linux 4.14+ rmnet support was upstreamed and ARHRD_RAWIP became 519,
+ // but it is 530 on at least some Qualcomm Android 4.9 kernels with rmnet
+ // example: Pixel 3 family
+ case 530:
+ // >5.4 kernels are GKI2.0 and thus upstream compatible, however 5.10
+ // shipped with Android S, so (for safety) let's limit ourselves to
+ // >5.10, ie. 5.11+ as a guarantee we're on Android T+ and thus no
+ // longer need this non-upstream compatibility logic
+ static bool is_pre_5_11_kernel = !isAtLeastKernelVersion(5, 11, 0);
+ if (is_pre_5_11_kernel)
+ return false;
+ }
+
+ switch (rv) {
+ case ARPHRD_ETHER:
+ isEthernet = true;
+ return 0;
+ case ARPHRD_NONE:
+ case ARPHRD_PPP:
+ case ARPHRD_RAWIP:
+ isEthernet = false;
+ return 0;
+ default:
+ ALOGE("Unknown hardware address type %d on interface %s", rv, iface);
+ return -EAFNOSUPPORT;
+ }
+}
+
+// ADD: nlMsgType=RTM_NEWQDISC nlMsgFlags=NLM_F_EXCL|NLM_F_CREATE
+// REPLACE: nlMsgType=RTM_NEWQDISC nlMsgFlags=NLM_F_CREATE|NLM_F_REPLACE
+// DEL: nlMsgType=RTM_DELQDISC nlMsgFlags=0
+int doTcQdiscClsact(int ifIndex, uint16_t nlMsgType, uint16_t nlMsgFlags) {
+ // This is the name of the qdisc we are attaching.
+ // Some hoop jumping to make this compile time constant with known size,
+ // so that the structure declaration is well defined at compile time.
+#define CLSACT "clsact"
+ // sizeof() includes the terminating NULL
+ static constexpr size_t ASCIIZ_LEN_CLSACT = sizeof(CLSACT);
+
+ const struct {
+ nlmsghdr n;
+ tcmsg t;
+ struct {
+ nlattr attr;
+ char str[NLMSG_ALIGN(ASCIIZ_LEN_CLSACT)];
+ } kind;
+ } req = {
+ .n =
+ {
+ .nlmsg_len = sizeof(req),
+ .nlmsg_type = nlMsgType,
+ .nlmsg_flags =
+ static_cast<__u16>(NETLINK_REQUEST_FLAGS | nlMsgFlags),
+ },
+ .t =
+ {
+ .tcm_family = AF_UNSPEC,
+ .tcm_ifindex = ifIndex,
+ .tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0),
+ .tcm_parent = TC_H_CLSACT,
+ },
+ .kind =
+ {
+ .attr =
+ {
+ .nla_len = NLA_HDRLEN + ASCIIZ_LEN_CLSACT,
+ .nla_type = TCA_KIND,
+ },
+ .str = CLSACT,
+ },
+ };
+#undef CLSACT
+
+ return sendAndProcessNetlinkResponse(&req, sizeof(req));
+}
+
+// tc filter add dev .. in/egress prio 1 protocol ipv6/ip bpf object-pinned
+// /sys/fs/bpf/... direct-action
+int tcAddBpfFilter(int ifIndex, bool ingress, uint16_t prio, uint16_t proto,
+ const char *bpfProgPath) {
+ const int bpfFd = bpf::retrieveProgram(bpfProgPath);
+ if (bpfFd == -1) {
+ ALOGE("retrieveProgram failed: %d", errno);
+ return -errno;
+ }
+ auto scopeGuard = base::make_scope_guard([bpfFd] { close(bpfFd); });
+
+ struct {
+ nlmsghdr n;
+ tcmsg t;
+ struct {
+ nlattr attr;
+ // The maximum classifier name length is defined in
+ // tcf_proto_ops in include/net/sch_generic.h.
+ char str[NLMSG_ALIGN(sizeof(CLS_BPF_KIND_NAME))];
+ } kind;
+ struct {
+ nlattr attr;
+ struct {
+ nlattr attr;
+ __u32 u32;
+ } fd;
+ struct {
+ nlattr attr;
+ char str[NLMSG_ALIGN(CLS_BPF_NAME_LEN)];
+ } name;
+ struct {
+ nlattr attr;
+ __u32 u32;
+ } flags;
+ } options;
+ } req = {
+ .n =
+ {
+ .nlmsg_len = sizeof(req),
+ .nlmsg_type = RTM_NEWTFILTER,
+ .nlmsg_flags = NETLINK_REQUEST_FLAGS | NLM_F_EXCL | NLM_F_CREATE,
+ },
+ .t =
+ {
+ .tcm_family = AF_UNSPEC,
+ .tcm_ifindex = ifIndex,
+ .tcm_handle = TC_H_UNSPEC,
+ .tcm_parent = TC_H_MAKE(TC_H_CLSACT, ingress ? TC_H_MIN_INGRESS
+ : TC_H_MIN_EGRESS),
+ .tcm_info =
+ static_cast<__u32>((static_cast<uint16_t>(prio) << 16) |
+ htons(static_cast<uint16_t>(proto))),
+ },
+ .kind =
+ {
+ .attr =
+ {
+ .nla_len = sizeof(req.kind),
+ .nla_type = TCA_KIND,
+ },
+ .str = CLS_BPF_KIND_NAME,
+ },
+ .options =
+ {
+ .attr =
+ {
+ .nla_len = sizeof(req.options),
+ .nla_type = NLA_F_NESTED | TCA_OPTIONS,
+ },
+ .fd =
+ {
+ .attr =
+ {
+ .nla_len = sizeof(req.options.fd),
+ .nla_type = TCA_BPF_FD,
+ },
+ .u32 = static_cast<__u32>(bpfFd),
+ },
+ .name =
+ {
+ .attr =
+ {
+ .nla_len = sizeof(req.options.name),
+ .nla_type = TCA_BPF_NAME,
+ },
+ // Visible via 'tc filter show', but
+ // is overwritten by strncpy below
+ .str = "placeholder",
+ },
+ .flags =
+ {
+ .attr =
+ {
+ .nla_len = sizeof(req.options.flags),
+ .nla_type = TCA_BPF_FLAGS,
+ },
+ .u32 = TCA_BPF_FLAG_ACT_DIRECT,
+ },
+ },
+ };
+
+ snprintf(req.options.name.str, sizeof(req.options.name.str), "%s:[*fsobj]",
+ basename(bpfProgPath));
+
+ int error = sendAndProcessNetlinkResponse(&req, sizeof(req));
+ return error;
+}
+
+// tc filter add dev .. ingress prio .. protocol .. matchall \
+// action police rate .. burst .. conform-exceed pipe/continue \
+// action bpf object-pinned .. \
+// drop
+//
+// TODO: tc-police does not do ECN marking, so in the future, we should consider
+// adding a second tc-police filter at a lower priority that rate limits traffic
+// at something like 0.8 times the global rate limit and ecn marks exceeding
+// packets inside a bpf program (but does not drop them).
+int tcAddIngressPoliceFilter(int ifIndex, uint16_t prio, uint16_t proto,
+ unsigned rateInBytesPerSec,
+ const char *bpfProgPath) {
+ // TODO: this value needs to be validated.
+ // TCP IW10 (initial congestion window) means servers will send 10 mtus worth
+ // of data on initial connect.
+ // If nic is LRO capable it could aggregate up to 64KiB, so again probably a
+ // bad idea to set burst below that, because ingress packets could get
+ // aggregated to 64KiB at the nic.
+ // I don't know, but I wonder whether we shouldn't just do 128KiB and not do
+ // any math.
+ static constexpr unsigned BURST_SIZE_IN_BYTES = 128 * 1024; // 128KiB
+ IngressPoliceFilterBuilder filter(ifIndex, prio, proto, rateInBytesPerSec,
+ BURST_SIZE_IN_BYTES, bpfProgPath);
+ const int error = filter.build();
+ if (error) {
+ return error;
+ }
+ return sendAndProcessNetlinkResponse(filter.getRequest(),
+ filter.getRequestSize());
+}
+
+// tc filter del dev .. in/egress prio .. protocol ..
+int tcDeleteFilter(int ifIndex, bool ingress, uint16_t prio, uint16_t proto) {
+ const struct {
+ nlmsghdr n;
+ tcmsg t;
+ } req = {
+ .n =
+ {
+ .nlmsg_len = sizeof(req),
+ .nlmsg_type = RTM_DELTFILTER,
+ .nlmsg_flags = NETLINK_REQUEST_FLAGS,
+ },
+ .t =
+ {
+ .tcm_family = AF_UNSPEC,
+ .tcm_ifindex = ifIndex,
+ .tcm_handle = TC_H_UNSPEC,
+ .tcm_parent = TC_H_MAKE(TC_H_CLSACT, ingress ? TC_H_MIN_INGRESS
+ : TC_H_MIN_EGRESS),
+ .tcm_info =
+ static_cast<__u32>((static_cast<uint16_t>(prio) << 16) |
+ htons(static_cast<uint16_t>(proto))),
+ },
+ };
+
+ return sendAndProcessNetlinkResponse(&req, sizeof(req));
+}
+
+} // namespace android
diff --git a/common/native/tcutils/tests/tcutils_test.cpp b/common/native/tcutils/tests/tcutils_test.cpp
new file mode 100644
index 0000000..32736d6
--- /dev/null
+++ b/common/native/tcutils/tests/tcutils_test.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * TcUtilsTest.cpp - unit tests for TcUtils.cpp
+ */
+
+#include <gtest/gtest.h>
+
+#include "kernelversion.h"
+#include <tcutils/tcutils.h>
+
+#include <BpfSyscallWrappers.h>
+#include <errno.h>
+#include <linux/if_ether.h>
+
+namespace android {
+
+TEST(LibTcUtilsTest, IsEthernetOfNonExistingIf) {
+ bool result = false;
+ int error = isEthernet("not_existing_if", result);
+ ASSERT_FALSE(result);
+ ASSERT_EQ(-ENODEV, error);
+}
+
+TEST(LibTcUtilsTest, IsEthernetOfLoopback) {
+ bool result = false;
+ int error = isEthernet("lo", result);
+ ASSERT_FALSE(result);
+ ASSERT_EQ(-EAFNOSUPPORT, error);
+}
+
+// If wireless 'wlan0' interface exists it should be Ethernet.
+// See also HardwareAddressTypeOfWireless.
+TEST(LibTcUtilsTest, IsEthernetOfWireless) {
+ bool result = false;
+ int error = isEthernet("wlan0", result);
+ if (!result && error == -ENODEV)
+ return;
+
+ ASSERT_EQ(0, error);
+ ASSERT_TRUE(result);
+}
+
+// If cellular 'rmnet_data0' interface exists it should
+// *probably* not be Ethernet and instead be RawIp.
+// See also HardwareAddressTypeOfCellular.
+TEST(LibTcUtilsTest, IsEthernetOfCellular) {
+ bool result = false;
+ int error = isEthernet("rmnet_data0", result);
+ if (!result && error == -ENODEV)
+ return;
+
+ ASSERT_EQ(0, error);
+ ASSERT_FALSE(result);
+}
+
+// See Linux kernel source in include/net/flow.h
+static constexpr int LOOPBACK_IFINDEX = 1;
+
+TEST(LibTcUtilsTest, AttachReplaceDetachClsactLo) {
+ // This attaches and detaches a configuration-less and thus no-op clsact
+ // qdisc to loopback interface (and it takes fractions of a second)
+ EXPECT_EQ(0, tcAddQdiscClsact(LOOPBACK_IFINDEX));
+ EXPECT_EQ(0, tcReplaceQdiscClsact(LOOPBACK_IFINDEX));
+ EXPECT_EQ(0, tcDeleteQdiscClsact(LOOPBACK_IFINDEX));
+ EXPECT_EQ(-EINVAL, tcDeleteQdiscClsact(LOOPBACK_IFINDEX));
+}
+
+TEST(LibTcUtilsTest, AddAndDeleteBpfFilter) {
+ // TODO: this should use bpf_shared.h rather than hardcoding the path
+ static constexpr char bpfProgPath[] =
+ "/sys/fs/bpf/tethering/prog_offload_schedcls_tether_downstream6_ether";
+ const int errNOENT = isAtLeastKernelVersion(4, 19, 0) ? ENOENT : EINVAL;
+
+ // static test values
+ static constexpr bool ingress = true;
+ static constexpr uint16_t prio = 17;
+ static constexpr uint16_t proto = ETH_P_ALL;
+
+ // try to delete missing filter from missing qdisc
+ EXPECT_EQ(-EINVAL, tcDeleteFilter(LOOPBACK_IFINDEX, ingress, prio, proto));
+ // try to attach bpf filter to missing qdisc
+ EXPECT_EQ(-EINVAL, tcAddBpfFilter(LOOPBACK_IFINDEX, ingress, prio, proto,
+ bpfProgPath));
+ // add the clsact qdisc
+ EXPECT_EQ(0, tcAddQdiscClsact(LOOPBACK_IFINDEX));
+ // try to delete missing filter when there is a qdisc attached
+ EXPECT_EQ(-errNOENT, tcDeleteFilter(LOOPBACK_IFINDEX, ingress, prio, proto));
+ // add and delete a bpf filter
+ EXPECT_EQ(
+ 0, tcAddBpfFilter(LOOPBACK_IFINDEX, ingress, prio, proto, bpfProgPath));
+ EXPECT_EQ(0, tcDeleteFilter(LOOPBACK_IFINDEX, ingress, prio, proto));
+ // try to remove the same filter a second time
+ EXPECT_EQ(-errNOENT, tcDeleteFilter(LOOPBACK_IFINDEX, ingress, prio, proto));
+ // remove the clsact qdisc
+ EXPECT_EQ(0, tcDeleteQdiscClsact(LOOPBACK_IFINDEX));
+ // once again, try to delete missing filter from missing qdisc
+ EXPECT_EQ(-EINVAL, tcDeleteFilter(LOOPBACK_IFINDEX, ingress, prio, proto));
+}
+
+TEST(LibTcUtilsTest, AddAndDeleteIngressPoliceFilter) {
+ // TODO: this should use bpf_shared.h rather than hardcoding the path
+ static constexpr char bpfProgPath[] =
+ "/sys/fs/bpf/prog_netd_schedact_ingress_account";
+ int fd = bpf::retrieveProgram(bpfProgPath);
+ if (fd == -1) {
+ // ingress policing is not supported.
+ return;
+ }
+ close(fd);
+
+ const int errNOENT = isAtLeastKernelVersion(4, 19, 0) ? ENOENT : EINVAL;
+
+ // static test values
+ static constexpr unsigned rateInBytesPerSec =
+ 1024 * 1024; // 8mbit/s => 1mbyte/s => 1024*1024 bytes/s.
+ static constexpr uint16_t prio = 17;
+ static constexpr uint16_t proto = ETH_P_ALL;
+
+ // try to delete missing filter from missing qdisc
+ EXPECT_EQ(-EINVAL,
+ tcDeleteFilter(LOOPBACK_IFINDEX, true /*ingress*/, prio, proto));
+ // try to attach bpf filter to missing qdisc
+ EXPECT_EQ(-EINVAL, tcAddIngressPoliceFilter(LOOPBACK_IFINDEX, prio, proto,
+ rateInBytesPerSec, bpfProgPath));
+ // add the clsact qdisc
+ EXPECT_EQ(0, tcAddQdiscClsact(LOOPBACK_IFINDEX));
+ // try to delete missing filter when there is a qdisc attached
+ EXPECT_EQ(-errNOENT,
+ tcDeleteFilter(LOOPBACK_IFINDEX, true /*ingress*/, prio, proto));
+ // add and delete a bpf filter
+ EXPECT_EQ(0, tcAddIngressPoliceFilter(LOOPBACK_IFINDEX, prio, proto,
+ rateInBytesPerSec, bpfProgPath));
+ EXPECT_EQ(0, tcDeleteFilter(LOOPBACK_IFINDEX, true /*ingress*/, prio, proto));
+ // try to remove the same filter a second time
+ EXPECT_EQ(-errNOENT,
+ tcDeleteFilter(LOOPBACK_IFINDEX, true /*ingress*/, prio, proto));
+ // remove the clsact qdisc
+ EXPECT_EQ(0, tcDeleteQdiscClsact(LOOPBACK_IFINDEX));
+ // once again, try to delete missing filter from missing qdisc
+ EXPECT_EQ(-EINVAL,
+ tcDeleteFilter(LOOPBACK_IFINDEX, true /*ingress*/, prio, proto));
+}
+
+} // namespace android
diff --git a/common/netd/Android.bp b/common/netd/Android.bp
index c3f9374..e249e19 100644
--- a/common/netd/Android.bp
+++ b/common/netd/Android.bp
@@ -21,7 +21,7 @@
sdk_version: "system_current",
min_sdk_version: "29",
static_libs: [
- "netd_aidl_interface-V7-java",
+ "netd_aidl_interface-V8-java",
],
apex_available: [
"//apex_available:platform", // used from services.net
@@ -44,10 +44,11 @@
cc_library_static {
name: "netd_aidl_interface-lateststable-ndk",
whole_static_libs: [
- "netd_aidl_interface-V7-ndk",
+ "netd_aidl_interface-V8-ndk",
],
apex_available: [
"com.android.resolv",
+ "com.android.tethering",
],
min_sdk_version: "29",
}
@@ -55,7 +56,7 @@
cc_library_static {
name: "netd_aidl_interface-lateststable-cpp",
whole_static_libs: [
- "netd_aidl_interface-V7-cpp",
+ "netd_aidl_interface-V8-cpp",
],
}
@@ -97,6 +98,7 @@
ndk: {
apex_available: [
"//apex_available:platform",
+ "com.android.tethering",
],
// This is necessary for the DnsResovler tests to run in Android Q.
// Soong would recognize this value and produce the Q compatible aidl library.
@@ -111,6 +113,7 @@
"5",
"6",
"7",
+ "8",
],
}
diff --git a/common/netd/aidl_api/netd_aidl_interface/8/.hash b/common/netd/aidl_api/netd_aidl_interface/8/.hash
new file mode 100644
index 0000000..0933816
--- /dev/null
+++ b/common/netd/aidl_api/netd_aidl_interface/8/.hash
@@ -0,0 +1 @@
+e8cf8586fc5da9063818d8775e9a21c4b0addb5b
diff --git a/common/netd/aidl_api/netd_aidl_interface/8/android/net/INetd.aidl b/common/netd/aidl_api/netd_aidl_interface/8/android/net/INetd.aidl
new file mode 100644
index 0000000..ec03d86
--- /dev/null
+++ b/common/netd/aidl_api/netd_aidl_interface/8/android/net/INetd.aidl
@@ -0,0 +1,200 @@
+/**
+ * Copyright (c) 2016, 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+interface INetd {
+ boolean isAlive();
+ boolean firewallReplaceUidChain(in @utf8InCpp String chainName, boolean isAllowlist, in int[] uids);
+ boolean bandwidthEnableDataSaver(boolean enable);
+ /**
+ * @deprecated use networkCreate() instead.
+ */
+ void networkCreatePhysical(int netId, int permission);
+ /**
+ * @deprecated use networkCreate() instead.
+ */
+ void networkCreateVpn(int netId, boolean secure);
+ void networkDestroy(int netId);
+ void networkAddInterface(int netId, in @utf8InCpp String iface);
+ void networkRemoveInterface(int netId, in @utf8InCpp String iface);
+ void networkAddUidRanges(int netId, in android.net.UidRangeParcel[] uidRanges);
+ void networkRemoveUidRanges(int netId, in android.net.UidRangeParcel[] uidRanges);
+ void networkRejectNonSecureVpn(boolean add, in android.net.UidRangeParcel[] uidRanges);
+ void socketDestroy(in android.net.UidRangeParcel[] uidRanges, in int[] exemptUids);
+ boolean tetherApplyDnsInterfaces();
+ android.net.TetherStatsParcel[] tetherGetStats();
+ void interfaceAddAddress(in @utf8InCpp String ifName, in @utf8InCpp String addrString, int prefixLength);
+ void interfaceDelAddress(in @utf8InCpp String ifName, in @utf8InCpp String addrString, int prefixLength);
+ @utf8InCpp String getProcSysNet(int ipversion, int which, in @utf8InCpp String ifname, in @utf8InCpp String parameter);
+ void setProcSysNet(int ipversion, int which, in @utf8InCpp String ifname, in @utf8InCpp String parameter, in @utf8InCpp String value);
+ void ipSecSetEncapSocketOwner(in ParcelFileDescriptor socket, int newUid);
+ int ipSecAllocateSpi(int transformId, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi);
+ void ipSecAddSecurityAssociation(int transformId, int mode, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int underlyingNetId, int spi, int markValue, int markMask, in @utf8InCpp String authAlgo, in byte[] authKey, in int authTruncBits, in @utf8InCpp String cryptAlgo, in byte[] cryptKey, in int cryptTruncBits, in @utf8InCpp String aeadAlgo, in byte[] aeadKey, in int aeadIcvBits, int encapType, int encapLocalPort, int encapRemotePort, int interfaceId);
+ void ipSecDeleteSecurityAssociation(int transformId, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi, int markValue, int markMask, int interfaceId);
+ void ipSecApplyTransportModeTransform(in ParcelFileDescriptor socket, int transformId, int direction, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi);
+ void ipSecRemoveTransportModeTransform(in ParcelFileDescriptor socket);
+ void ipSecAddSecurityPolicy(int transformId, int selAddrFamily, int direction, in @utf8InCpp String tmplSrcAddress, in @utf8InCpp String tmplDstAddress, int spi, int markValue, int markMask, int interfaceId);
+ void ipSecUpdateSecurityPolicy(int transformId, int selAddrFamily, int direction, in @utf8InCpp String tmplSrcAddress, in @utf8InCpp String tmplDstAddress, int spi, int markValue, int markMask, int interfaceId);
+ void ipSecDeleteSecurityPolicy(int transformId, int selAddrFamily, int direction, int markValue, int markMask, int interfaceId);
+ void ipSecAddTunnelInterface(in @utf8InCpp String deviceName, in @utf8InCpp String localAddress, in @utf8InCpp String remoteAddress, int iKey, int oKey, int interfaceId);
+ void ipSecUpdateTunnelInterface(in @utf8InCpp String deviceName, in @utf8InCpp String localAddress, in @utf8InCpp String remoteAddress, int iKey, int oKey, int interfaceId);
+ void ipSecRemoveTunnelInterface(in @utf8InCpp String deviceName);
+ void wakeupAddInterface(in @utf8InCpp String ifName, in @utf8InCpp String prefix, int mark, int mask);
+ void wakeupDelInterface(in @utf8InCpp String ifName, in @utf8InCpp String prefix, int mark, int mask);
+ void setIPv6AddrGenMode(in @utf8InCpp String ifName, int mode);
+ void idletimerAddInterface(in @utf8InCpp String ifName, int timeout, in @utf8InCpp String classLabel);
+ void idletimerRemoveInterface(in @utf8InCpp String ifName, int timeout, in @utf8InCpp String classLabel);
+ void strictUidCleartextPenalty(int uid, int policyPenalty);
+ @utf8InCpp String clatdStart(in @utf8InCpp String ifName, in @utf8InCpp String nat64Prefix);
+ void clatdStop(in @utf8InCpp String ifName);
+ boolean ipfwdEnabled();
+ @utf8InCpp String[] ipfwdGetRequesterList();
+ void ipfwdEnableForwarding(in @utf8InCpp String requester);
+ void ipfwdDisableForwarding(in @utf8InCpp String requester);
+ void ipfwdAddInterfaceForward(in @utf8InCpp String fromIface, in @utf8InCpp String toIface);
+ void ipfwdRemoveInterfaceForward(in @utf8InCpp String fromIface, in @utf8InCpp String toIface);
+ void bandwidthSetInterfaceQuota(in @utf8InCpp String ifName, long bytes);
+ void bandwidthRemoveInterfaceQuota(in @utf8InCpp String ifName);
+ void bandwidthSetInterfaceAlert(in @utf8InCpp String ifName, long bytes);
+ void bandwidthRemoveInterfaceAlert(in @utf8InCpp String ifName);
+ void bandwidthSetGlobalAlert(long bytes);
+ void bandwidthAddNaughtyApp(int uid);
+ void bandwidthRemoveNaughtyApp(int uid);
+ void bandwidthAddNiceApp(int uid);
+ void bandwidthRemoveNiceApp(int uid);
+ void tetherStart(in @utf8InCpp String[] dhcpRanges);
+ void tetherStop();
+ boolean tetherIsEnabled();
+ void tetherInterfaceAdd(in @utf8InCpp String ifName);
+ void tetherInterfaceRemove(in @utf8InCpp String ifName);
+ @utf8InCpp String[] tetherInterfaceList();
+ void tetherDnsSet(int netId, in @utf8InCpp String[] dnsAddrs);
+ @utf8InCpp String[] tetherDnsList();
+ void networkAddRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop);
+ void networkRemoveRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop);
+ void networkAddLegacyRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop, int uid);
+ void networkRemoveLegacyRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop, int uid);
+ int networkGetDefault();
+ void networkSetDefault(int netId);
+ void networkClearDefault();
+ void networkSetPermissionForNetwork(int netId, int permission);
+ void networkSetPermissionForUser(int permission, in int[] uids);
+ void networkClearPermissionForUser(in int[] uids);
+ void trafficSetNetPermForUids(int permission, in int[] uids);
+ void networkSetProtectAllow(int uid);
+ void networkSetProtectDeny(int uid);
+ boolean networkCanProtect(int uid);
+ void firewallSetFirewallType(int firewalltype);
+ void firewallSetInterfaceRule(in @utf8InCpp String ifName, int firewallRule);
+ void firewallSetUidRule(int childChain, int uid, int firewallRule);
+ void firewallEnableChildChain(int childChain, boolean enable);
+ @utf8InCpp String[] interfaceGetList();
+ android.net.InterfaceConfigurationParcel interfaceGetCfg(in @utf8InCpp String ifName);
+ void interfaceSetCfg(in android.net.InterfaceConfigurationParcel cfg);
+ void interfaceSetIPv6PrivacyExtensions(in @utf8InCpp String ifName, boolean enable);
+ void interfaceClearAddrs(in @utf8InCpp String ifName);
+ void interfaceSetEnableIPv6(in @utf8InCpp String ifName, boolean enable);
+ void interfaceSetMtu(in @utf8InCpp String ifName, int mtu);
+ void tetherAddForward(in @utf8InCpp String intIface, in @utf8InCpp String extIface);
+ void tetherRemoveForward(in @utf8InCpp String intIface, in @utf8InCpp String extIface);
+ void setTcpRWmemorySize(in @utf8InCpp String rmemValues, in @utf8InCpp String wmemValues);
+ void registerUnsolicitedEventListener(android.net.INetdUnsolicitedEventListener listener);
+ void firewallAddUidInterfaceRules(in @utf8InCpp String ifName, in int[] uids);
+ void firewallRemoveUidInterfaceRules(in int[] uids);
+ void trafficSwapActiveStatsMap();
+ IBinder getOemNetd();
+ void tetherStartWithConfiguration(in android.net.TetherConfigParcel config);
+ android.net.MarkMaskParcel getFwmarkForNetwork(int netId);
+ void networkAddRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+ void networkUpdateRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+ void networkRemoveRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+ void tetherOffloadRuleAdd(in android.net.TetherOffloadRuleParcel rule);
+ void tetherOffloadRuleRemove(in android.net.TetherOffloadRuleParcel rule);
+ android.net.TetherStatsParcel[] tetherOffloadGetStats();
+ void tetherOffloadSetInterfaceQuota(int ifIndex, long quotaBytes);
+ android.net.TetherStatsParcel tetherOffloadGetAndClearStats(int ifIndex);
+ void networkCreate(in android.net.NativeNetworkConfig config);
+ void networkAddUidRangesParcel(in android.net.netd.aidl.NativeUidRangeConfig uidRangesConfig);
+ void networkRemoveUidRangesParcel(in android.net.netd.aidl.NativeUidRangeConfig uidRangesConfig);
+ const int IPV4 = 4;
+ const int IPV6 = 6;
+ const int CONF = 1;
+ const int NEIGH = 2;
+ const String IPSEC_INTERFACE_PREFIX = "ipsec";
+ const int IPV6_ADDR_GEN_MODE_EUI64 = 0;
+ const int IPV6_ADDR_GEN_MODE_NONE = 1;
+ const int IPV6_ADDR_GEN_MODE_STABLE_PRIVACY = 2;
+ const int IPV6_ADDR_GEN_MODE_RANDOM = 3;
+ const int IPV6_ADDR_GEN_MODE_DEFAULT = 0;
+ const int PENALTY_POLICY_ACCEPT = 1;
+ const int PENALTY_POLICY_LOG = 2;
+ const int PENALTY_POLICY_REJECT = 3;
+ const int LOCAL_NET_ID = 99;
+ const int DUMMY_NET_ID = 51;
+ const int UNREACHABLE_NET_ID = 52;
+ const String NEXTHOP_NONE = "";
+ const String NEXTHOP_UNREACHABLE = "unreachable";
+ const String NEXTHOP_THROW = "throw";
+ const int PERMISSION_NONE = 0;
+ const int PERMISSION_NETWORK = 1;
+ const int PERMISSION_SYSTEM = 2;
+ const int NO_PERMISSIONS = 0;
+ const int PERMISSION_INTERNET = 4;
+ const int PERMISSION_UPDATE_DEVICE_STATS = 8;
+ const int PERMISSION_UNINSTALLED = -1;
+ /**
+ * @deprecated use FIREWALL_ALLOWLIST.
+ */
+ const int FIREWALL_WHITELIST = 0;
+ const int FIREWALL_ALLOWLIST = 0;
+ /**
+ * @deprecated use FIREWALL_DENYLIST.
+ */
+ const int FIREWALL_BLACKLIST = 1;
+ const int FIREWALL_DENYLIST = 1;
+ const int FIREWALL_RULE_ALLOW = 1;
+ const int FIREWALL_RULE_DENY = 2;
+ const int FIREWALL_CHAIN_NONE = 0;
+ const int FIREWALL_CHAIN_DOZABLE = 1;
+ const int FIREWALL_CHAIN_STANDBY = 2;
+ const int FIREWALL_CHAIN_POWERSAVE = 3;
+ const int FIREWALL_CHAIN_RESTRICTED = 4;
+ const String IF_STATE_UP = "up";
+ const String IF_STATE_DOWN = "down";
+ const String IF_FLAG_BROADCAST = "broadcast";
+ const String IF_FLAG_LOOPBACK = "loopback";
+ const String IF_FLAG_POINTOPOINT = "point-to-point";
+ const String IF_FLAG_RUNNING = "running";
+ const String IF_FLAG_MULTICAST = "multicast";
+}
diff --git a/common/netd/aidl_api/netd_aidl_interface/8/android/net/INetdUnsolicitedEventListener.aidl b/common/netd/aidl_api/netd_aidl_interface/8/android/net/INetdUnsolicitedEventListener.aidl
new file mode 100644
index 0000000..31775df
--- /dev/null
+++ b/common/netd/aidl_api/netd_aidl_interface/8/android/net/INetdUnsolicitedEventListener.aidl
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2018, 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+interface INetdUnsolicitedEventListener {
+ oneway void onInterfaceClassActivityChanged(boolean isActive, int timerLabel, long timestampNs, int uid);
+ oneway void onQuotaLimitReached(@utf8InCpp String alertName, @utf8InCpp String ifName);
+ oneway void onInterfaceDnsServerInfo(@utf8InCpp String ifName, long lifetimeS, in @utf8InCpp String[] servers);
+ oneway void onInterfaceAddressUpdated(@utf8InCpp String addr, @utf8InCpp String ifName, int flags, int scope);
+ oneway void onInterfaceAddressRemoved(@utf8InCpp String addr, @utf8InCpp String ifName, int flags, int scope);
+ oneway void onInterfaceAdded(@utf8InCpp String ifName);
+ oneway void onInterfaceRemoved(@utf8InCpp String ifName);
+ oneway void onInterfaceChanged(@utf8InCpp String ifName, boolean up);
+ oneway void onInterfaceLinkStateChanged(@utf8InCpp String ifName, boolean up);
+ oneway void onRouteChanged(boolean updated, @utf8InCpp String route, @utf8InCpp String gateway, @utf8InCpp String ifName);
+ oneway void onStrictCleartextDetected(int uid, @utf8InCpp String hex);
+}
diff --git a/common/netd/aidl_api/netd_aidl_interface/8/android/net/InterfaceConfigurationParcel.aidl b/common/netd/aidl_api/netd_aidl_interface/8/android/net/InterfaceConfigurationParcel.aidl
new file mode 100644
index 0000000..1869d8d
--- /dev/null
+++ b/common/netd/aidl_api/netd_aidl_interface/8/android/net/InterfaceConfigurationParcel.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable InterfaceConfigurationParcel {
+ @utf8InCpp String ifName;
+ @utf8InCpp String hwAddr;
+ @utf8InCpp String ipv4Addr;
+ int prefixLength;
+ @utf8InCpp String[] flags;
+}
diff --git a/common/netd/aidl_api/netd_aidl_interface/8/android/net/MarkMaskParcel.aidl b/common/netd/aidl_api/netd_aidl_interface/8/android/net/MarkMaskParcel.aidl
new file mode 100644
index 0000000..8ea20d1
--- /dev/null
+++ b/common/netd/aidl_api/netd_aidl_interface/8/android/net/MarkMaskParcel.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable MarkMaskParcel {
+ int mark;
+ int mask;
+}
diff --git a/common/netd/aidl_api/netd_aidl_interface/8/android/net/NativeNetworkConfig.aidl b/common/netd/aidl_api/netd_aidl_interface/8/android/net/NativeNetworkConfig.aidl
new file mode 100644
index 0000000..77d814b
--- /dev/null
+++ b/common/netd/aidl_api/netd_aidl_interface/8/android/net/NativeNetworkConfig.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable
+parcelable NativeNetworkConfig {
+ int netId;
+ android.net.NativeNetworkType networkType = android.net.NativeNetworkType.PHYSICAL;
+ int permission;
+ boolean secure;
+ android.net.NativeVpnType vpnType = android.net.NativeVpnType.PLATFORM;
+ boolean excludeLocalRoutes = false;
+}
diff --git a/common/netd/aidl_api/netd_aidl_interface/8/android/net/NativeNetworkType.aidl b/common/netd/aidl_api/netd_aidl_interface/8/android/net/NativeNetworkType.aidl
new file mode 100644
index 0000000..06c8979
--- /dev/null
+++ b/common/netd/aidl_api/netd_aidl_interface/8/android/net/NativeNetworkType.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+@Backing(type="int")
+enum NativeNetworkType {
+ PHYSICAL = 0,
+ VIRTUAL = 1,
+}
diff --git a/common/netd/aidl_api/netd_aidl_interface/8/android/net/NativeVpnType.aidl b/common/netd/aidl_api/netd_aidl_interface/8/android/net/NativeVpnType.aidl
new file mode 100644
index 0000000..8a8be83
--- /dev/null
+++ b/common/netd/aidl_api/netd_aidl_interface/8/android/net/NativeVpnType.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+@Backing(type="int")
+enum NativeVpnType {
+ SERVICE = 1,
+ PLATFORM = 2,
+ LEGACY = 3,
+ OEM = 4,
+}
diff --git a/common/netd/aidl_api/netd_aidl_interface/8/android/net/RouteInfoParcel.aidl b/common/netd/aidl_api/netd_aidl_interface/8/android/net/RouteInfoParcel.aidl
new file mode 100644
index 0000000..5ef95e6
--- /dev/null
+++ b/common/netd/aidl_api/netd_aidl_interface/8/android/net/RouteInfoParcel.aidl
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+parcelable RouteInfoParcel {
+ @utf8InCpp String destination;
+ @utf8InCpp String ifName;
+ @utf8InCpp String nextHop;
+ int mtu;
+}
diff --git a/common/netd/aidl_api/netd_aidl_interface/8/android/net/TetherConfigParcel.aidl b/common/netd/aidl_api/netd_aidl_interface/8/android/net/TetherConfigParcel.aidl
new file mode 100644
index 0000000..7b39c22
--- /dev/null
+++ b/common/netd/aidl_api/netd_aidl_interface/8/android/net/TetherConfigParcel.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable TetherConfigParcel {
+ boolean usingLegacyDnsProxy;
+ @utf8InCpp String[] dhcpRanges;
+}
diff --git a/common/netd/aidl_api/netd_aidl_interface/8/android/net/TetherOffloadRuleParcel.aidl b/common/netd/aidl_api/netd_aidl_interface/8/android/net/TetherOffloadRuleParcel.aidl
new file mode 100644
index 0000000..983e986
--- /dev/null
+++ b/common/netd/aidl_api/netd_aidl_interface/8/android/net/TetherOffloadRuleParcel.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable TetherOffloadRuleParcel {
+ int inputInterfaceIndex;
+ int outputInterfaceIndex;
+ byte[] destination;
+ int prefixLength;
+ byte[] srcL2Address;
+ byte[] dstL2Address;
+ int pmtu = 1500;
+}
diff --git a/common/netd/aidl_api/netd_aidl_interface/8/android/net/TetherStatsParcel.aidl b/common/netd/aidl_api/netd_aidl_interface/8/android/net/TetherStatsParcel.aidl
new file mode 100644
index 0000000..5f1b722
--- /dev/null
+++ b/common/netd/aidl_api/netd_aidl_interface/8/android/net/TetherStatsParcel.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable TetherStatsParcel {
+ @utf8InCpp String iface;
+ long rxBytes;
+ long rxPackets;
+ long txBytes;
+ long txPackets;
+ int ifIndex = 0;
+}
diff --git a/common/netd/aidl_api/netd_aidl_interface/8/android/net/UidRangeParcel.aidl b/common/netd/aidl_api/netd_aidl_interface/8/android/net/UidRangeParcel.aidl
new file mode 100644
index 0000000..72e987a
--- /dev/null
+++ b/common/netd/aidl_api/netd_aidl_interface/8/android/net/UidRangeParcel.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable
+parcelable UidRangeParcel {
+ int start;
+ int stop;
+}
diff --git a/common/netd/aidl_api/netd_aidl_interface/8/android/net/netd/aidl/NativeUidRangeConfig.aidl b/common/netd/aidl_api/netd_aidl_interface/8/android/net/netd/aidl/NativeUidRangeConfig.aidl
new file mode 100644
index 0000000..9bb679f
--- /dev/null
+++ b/common/netd/aidl_api/netd_aidl_interface/8/android/net/netd/aidl/NativeUidRangeConfig.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.netd.aidl;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable
+parcelable NativeUidRangeConfig {
+ int netId;
+ android.net.UidRangeParcel[] uidRanges;
+ int subPriority;
+}
diff --git a/common/netd/aidl_api/netd_aidl_interface/current/android/net/NativeNetworkConfig.aidl b/common/netd/aidl_api/netd_aidl_interface/current/android/net/NativeNetworkConfig.aidl
index 76562b2..77d814b 100644
--- a/common/netd/aidl_api/netd_aidl_interface/current/android/net/NativeNetworkConfig.aidl
+++ b/common/netd/aidl_api/netd_aidl_interface/current/android/net/NativeNetworkConfig.aidl
@@ -40,4 +40,5 @@
int permission;
boolean secure;
android.net.NativeVpnType vpnType = android.net.NativeVpnType.PLATFORM;
+ boolean excludeLocalRoutes = false;
}
diff --git a/common/netd/binder/android/net/NativeNetworkConfig.aidl b/common/netd/binder/android/net/NativeNetworkConfig.aidl
index 2c4f83a..e654a1f 100644
--- a/common/netd/binder/android/net/NativeNetworkConfig.aidl
+++ b/common/netd/binder/android/net/NativeNetworkConfig.aidl
@@ -49,4 +49,9 @@
/** For virtual networks. The type of VPN to create. Ignored for all other network types. */
NativeVpnType vpnType = NativeVpnType.PLATFORM;
+
+ /**
+ * For virtual networks. Whether local traffic is excluded from the VPN.
+ */
+ boolean excludeLocalRoutes = false;
}
diff --git a/common/netd/libnetdutils/Android.bp b/common/netd/libnetdutils/Android.bp
index 732e37d..08d5412 100644
--- a/common/netd/libnetdutils/Android.bp
+++ b/common/netd/libnetdutils/Android.bp
@@ -11,6 +11,7 @@
"Log.cpp",
"Netfilter.cpp",
"Netlink.cpp",
+ "NetlinkListener.cpp",
"Slice.cpp",
"Socket.cpp",
"SocketOption.cpp",
diff --git a/common/netd/libnetdutils/NetlinkListener.cpp b/common/netd/libnetdutils/NetlinkListener.cpp
new file mode 100644
index 0000000..decaa9c
--- /dev/null
+++ b/common/netd/libnetdutils/NetlinkListener.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "NetlinkListener"
+
+#include <sstream>
+#include <vector>
+
+#include <linux/netfilter/nfnetlink.h>
+
+#include <log/log.h>
+#include <netdutils/Misc.h>
+#include <netdutils/NetlinkListener.h>
+#include <netdutils/Syscalls.h>
+
+namespace android {
+namespace netdutils {
+
+using netdutils::Fd;
+using netdutils::Slice;
+using netdutils::Status;
+using netdutils::UniqueFd;
+using netdutils::findWithDefault;
+using netdutils::forEachNetlinkMessage;
+using netdutils::makeSlice;
+using netdutils::sSyscalls;
+using netdutils::status::ok;
+using netdutils::statusFromErrno;
+
+namespace {
+
+constexpr int kNetlinkMsgErrorType = (NFNL_SUBSYS_NONE << 8) | NLMSG_ERROR;
+
+constexpr sockaddr_nl kKernelAddr = {
+ .nl_family = AF_NETLINK, .nl_pad = 0, .nl_pid = 0, .nl_groups = 0,
+};
+
+const NetlinkListener::DispatchFn kDefaultDispatchFn = [](const nlmsghdr& nlmsg, const Slice) {
+ std::stringstream ss;
+ ss << nlmsg;
+ ALOGE("unhandled netlink message: %s", ss.str().c_str());
+};
+
+} // namespace
+
+NetlinkListener::NetlinkListener(UniqueFd event, UniqueFd sock, const std::string& name)
+ : mEvent(std::move(event)), mSock(std::move(sock)), mThreadName(name) {
+ const auto rxErrorHandler = [](const nlmsghdr& nlmsg, const Slice msg) {
+ std::stringstream ss;
+ ss << nlmsg << " " << msg << " " << netdutils::toHex(msg, 32);
+ ALOGE("unhandled netlink message: %s", ss.str().c_str());
+ };
+ expectOk(NetlinkListener::subscribe(kNetlinkMsgErrorType, rxErrorHandler));
+
+ mErrorHandler = [& name = mThreadName](const int fd, const int err) {
+ ALOGE("Error on NetlinkListener(%s) fd=%d: %s", name.c_str(), fd, strerror(err));
+ };
+
+ // Start the thread
+ mWorker = std::thread([this]() { run().ignoreError(); });
+}
+
+NetlinkListener::~NetlinkListener() {
+ const auto& sys = sSyscalls.get();
+ const uint64_t data = 1;
+ // eventfd should never enter an error state unexpectedly
+ expectOk(sys.write(mEvent, makeSlice(data)).status());
+ mWorker.join();
+}
+
+Status NetlinkListener::send(const Slice msg) {
+ const auto& sys = sSyscalls.get();
+ ASSIGN_OR_RETURN(auto sent, sys.sendto(mSock, msg, 0, kKernelAddr));
+ if (sent != msg.size()) {
+ return statusFromErrno(EMSGSIZE, "unexpect message size");
+ }
+ return ok;
+}
+
+Status NetlinkListener::subscribe(uint16_t type, const DispatchFn& fn) {
+ std::lock_guard guard(mMutex);
+ mDispatchMap[type] = fn;
+ return ok;
+}
+
+Status NetlinkListener::unsubscribe(uint16_t type) {
+ std::lock_guard guard(mMutex);
+ mDispatchMap.erase(type);
+ return ok;
+}
+
+void NetlinkListener::registerSkErrorHandler(const SkErrorHandler& handler) {
+ mErrorHandler = handler;
+}
+
+Status NetlinkListener::run() {
+ std::vector<char> rxbuf(4096);
+
+ const auto rxHandler = [this](const nlmsghdr& nlmsg, const Slice& buf) {
+ std::lock_guard guard(mMutex);
+ const auto& fn = findWithDefault(mDispatchMap, nlmsg.nlmsg_type, kDefaultDispatchFn);
+ fn(nlmsg, buf);
+ };
+
+ if (mThreadName.length() > 0) {
+ int ret = pthread_setname_np(pthread_self(), mThreadName.c_str());
+ if (ret) {
+ ALOGE("thread name set failed, name: %s, ret: %s", mThreadName.c_str(), strerror(ret));
+ }
+ }
+ const auto& sys = sSyscalls.get();
+ const std::array<Fd, 2> fds{{{mEvent}, {mSock}}};
+ const int events = POLLIN;
+ const double timeout = 3600;
+ while (true) {
+ ASSIGN_OR_RETURN(auto revents, sys.ppoll(fds, events, timeout));
+ // After mEvent becomes readable, we should stop servicing mSock and return
+ if (revents[0] & POLLIN) {
+ break;
+ }
+ if (revents[1] & (POLLIN|POLLERR)) {
+ auto rx = sys.recvfrom(mSock, makeSlice(rxbuf), 0);
+ int err = rx.status().code();
+ if (err) {
+ // Ignore errors. The only error we expect to see here is ENOBUFS, and there's
+ // nothing we can do about that. The recvfrom above will already have cleared the
+ // error indication and ensured we won't get EPOLLERR again.
+ // TODO: Consider using NETLINK_NO_ENOBUFS.
+ mErrorHandler(((Fd) mSock).get(), err);
+ continue;
+ }
+ forEachNetlinkMessage(rx.value(), rxHandler);
+ }
+ }
+ return ok;
+}
+
+} // namespace netdutils
+} // namespace android
diff --git a/common/netd/libnetdutils/include/netdutils/NetlinkListener.h b/common/netd/libnetdutils/include/netdutils/NetlinkListener.h
new file mode 100644
index 0000000..97f7bb2
--- /dev/null
+++ b/common/netd/libnetdutils/include/netdutils/NetlinkListener.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef NETLINK_LISTENER_H
+#define NETLINK_LISTENER_H
+
+#include <functional>
+#include <map>
+#include <mutex>
+#include <thread>
+
+#include <android-base/thread_annotations.h>
+#include <netdutils/Netlink.h>
+#include <netdutils/Slice.h>
+#include <netdutils/Status.h>
+#include <netdutils/UniqueFd.h>
+
+namespace android {
+namespace netdutils {
+
+class NetlinkListenerInterface {
+ public:
+ using DispatchFn = std::function<void(const nlmsghdr& nlmsg, const netdutils::Slice msg)>;
+
+ using SkErrorHandler = std::function<void(const int fd, const int err)>;
+
+ virtual ~NetlinkListenerInterface() = default;
+
+ // Send message to the kernel using the underlying netlink socket
+ virtual netdutils::Status send(const netdutils::Slice msg) = 0;
+
+ // Deliver future messages with nlmsghdr.nlmsg_type == type to fn.
+ //
+ // Threadsafe.
+ // All dispatch functions invoked on a single service thread.
+ // subscribe() and join() must not be called from the stack of fn().
+ virtual netdutils::Status subscribe(uint16_t type, const DispatchFn& fn) = 0;
+
+ // Halt delivery of future messages with nlmsghdr.nlmsg_type == type.
+ // Threadsafe.
+ virtual netdutils::Status unsubscribe(uint16_t type) = 0;
+
+ virtual void registerSkErrorHandler(const SkErrorHandler& handler) = 0;
+};
+
+// NetlinkListener manages a netlink socket and associated blocking
+// service thread.
+//
+// This class is written in a generic way to allow multiple different
+// netlink subsystems to share this common infrastructure. If multiple
+// subsystems share the same message delivery requirements (drops ok,
+// no drops) they may share a single listener by calling subscribe()
+// with multiple types.
+//
+// This class is suitable for moderate performance message
+// processing. In particular it avoids extra copies of received
+// message data and allows client code to control which message
+// attributes are processed.
+//
+// Note that NetlinkListener is capable of processing multiple batched
+// netlink messages in a single system call. This is useful to
+// netfilter extensions that allow batching of events like NFLOG.
+class NetlinkListener : public NetlinkListenerInterface {
+ public:
+ NetlinkListener(netdutils::UniqueFd event, netdutils::UniqueFd sock, const std::string& name);
+
+ ~NetlinkListener() override;
+
+ netdutils::Status send(const netdutils::Slice msg) override;
+
+ netdutils::Status subscribe(uint16_t type, const DispatchFn& fn) override EXCLUDES(mMutex);
+
+ netdutils::Status unsubscribe(uint16_t type) override EXCLUDES(mMutex);
+
+ void registerSkErrorHandler(const SkErrorHandler& handler) override;
+
+ private:
+ netdutils::Status run();
+
+ const netdutils::UniqueFd mEvent;
+ const netdutils::UniqueFd mSock;
+ const std::string mThreadName;
+ std::mutex mMutex;
+ std::map<uint16_t, DispatchFn> mDispatchMap GUARDED_BY(mMutex);
+ std::thread mWorker;
+ SkErrorHandler mErrorHandler;
+};
+
+} // namespace netdutils
+} // namespace android
+
+#endif /* NETLINK_LISTENER_H */
diff --git a/common/tests/unit/src/com/android/net/module/util/NetworkCapabilitiesUtilsTest.kt b/common/tests/unit/src/com/android/net/module/util/NetworkCapabilitiesUtilsTest.kt
index f78c74e..256ea1e 100644
--- a/common/tests/unit/src/com/android/net/module/util/NetworkCapabilitiesUtilsTest.kt
+++ b/common/tests/unit/src/com/android/net/module/util/NetworkCapabilitiesUtilsTest.kt
@@ -18,6 +18,7 @@
import android.annotation.TargetApi
import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.NET_CAPABILITY_BIP
import android.net.NetworkCapabilities.NET_CAPABILITY_CBS
import android.net.NetworkCapabilities.NET_CAPABILITY_EIMS
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
@@ -33,6 +34,7 @@
import android.os.Build
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
+import com.android.modules.utils.build.SdkLevel
import com.android.net.module.util.NetworkCapabilitiesUtils.RESTRICTED_CAPABILITIES
import com.android.net.module.util.NetworkCapabilitiesUtils.UNRESTRICTED_CAPABILITIES
import com.android.net.module.util.NetworkCapabilitiesUtils.getDisplayTransport
@@ -110,6 +112,12 @@
// as restricted when there is no any unrestricted capability.
nc.removeCapability(NET_CAPABILITY_INTERNET)
assertTrue(NetworkCapabilitiesUtils.inferRestrictedCapability(nc))
+ if (!SdkLevel.isAtLeastS()) return
+ // BIP deserves its specific test because it's the first capability over 30, meaning the
+ // shift will overflow
+ nc.removeCapability(NET_CAPABILITY_CBS)
+ nc.addCapability(NET_CAPABILITY_BIP)
+ assertTrue(NetworkCapabilitiesUtils.inferRestrictedCapability(nc))
}
@Test
@@ -122,9 +130,17 @@
assertEquals((1 shl NET_CAPABILITY_CBS).toLong() and RESTRICTED_CAPABILITIES,
(1 shl NET_CAPABILITY_CBS).toLong())
+ // verify BIP is also restricted
+ // BIP is not available in R and before, but the BIP constant is inlined so
+ // this test can still run on R.
+ assertEquals((1L shl NET_CAPABILITY_BIP) and RESTRICTED_CAPABILITIES,
+ (1L shl NET_CAPABILITY_BIP))
+
// verify default is not restricted
assertEquals((1 shl NET_CAPABILITY_INTERNET).toLong() and RESTRICTED_CAPABILITIES, 0)
+ assertTrue(RESTRICTED_CAPABILITIES > 0)
+
// just to see
assertEquals(RESTRICTED_CAPABILITIES and UNRESTRICTED_CAPABILITIES, 0)
}
diff --git a/common/tests/unit/src/com/android/net/module/util/NetworkStatsUtilsTest.kt b/common/tests/unit/src/com/android/net/module/util/NetworkStatsUtilsTest.kt
index ff839fe..2785ea9 100644
--- a/common/tests/unit/src/com/android/net/module/util/NetworkStatsUtilsTest.kt
+++ b/common/tests/unit/src/com/android/net/module/util/NetworkStatsUtilsTest.kt
@@ -16,12 +16,16 @@
package com.android.net.module.util
+import android.net.NetworkStats
+import android.text.TextUtils
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
@RunWith(AndroidJUnit4::class)
@SmallTest
@@ -71,4 +75,68 @@
assertEquals(11, NetworkStatsUtils.constrain(11, 11, 11))
assertEquals(11, NetworkStatsUtils.constrain(1, 11, 11))
}
+
+ @Test
+ fun testBucketToEntry() {
+ val bucket = makeMockBucket(android.app.usage.NetworkStats.Bucket.UID_ALL,
+ android.app.usage.NetworkStats.Bucket.TAG_NONE,
+ android.app.usage.NetworkStats.Bucket.STATE_DEFAULT,
+ android.app.usage.NetworkStats.Bucket.METERED_YES,
+ android.app.usage.NetworkStats.Bucket.ROAMING_NO,
+ android.app.usage.NetworkStats.Bucket.DEFAULT_NETWORK_ALL, 1024, 8, 2048, 12)
+ val entry = NetworkStatsUtils.fromBucket(bucket)
+ val expectedEntry = NetworkStats.Entry(null /* IFACE_ALL */, NetworkStats.UID_ALL,
+ NetworkStats.SET_DEFAULT, NetworkStats.TAG_NONE, NetworkStats.METERED_YES,
+ NetworkStats.ROAMING_NO, NetworkStats.DEFAULT_NETWORK_ALL, 1024, 8, 2048, 12,
+ 0 /* operations */)
+
+ // TODO: Use assertEquals once all downstreams accept null iface in
+ // NetworkStats.Entry#equals.
+ assertEntryEquals(expectedEntry, entry)
+ }
+
+ private fun makeMockBucket(
+ uid: Int,
+ tag: Int,
+ state: Int,
+ metered: Int,
+ roaming: Int,
+ defaultNetwork: Int,
+ rxBytes: Long,
+ rxPackets: Long,
+ txBytes: Long,
+ txPackets: Long
+ ): android.app.usage.NetworkStats.Bucket {
+ val ret: android.app.usage.NetworkStats.Bucket =
+ mock(android.app.usage.NetworkStats.Bucket::class.java)
+ doReturn(uid).`when`(ret).getUid()
+ doReturn(tag).`when`(ret).getTag()
+ doReturn(state).`when`(ret).getState()
+ doReturn(metered).`when`(ret).getMetered()
+ doReturn(roaming).`when`(ret).getRoaming()
+ doReturn(defaultNetwork).`when`(ret).getDefaultNetworkStatus()
+ doReturn(rxBytes).`when`(ret).getRxBytes()
+ doReturn(rxPackets).`when`(ret).getRxPackets()
+ doReturn(txBytes).`when`(ret).getTxBytes()
+ doReturn(txPackets).`when`(ret).getTxPackets()
+ return ret
+ }
+
+ /**
+ * Assert that the two {@link NetworkStats.Entry} are equals.
+ */
+ private fun assertEntryEquals(left: NetworkStats.Entry, right: NetworkStats.Entry) {
+ TextUtils.equals(left.iface, right.iface)
+ assertEquals(left.uid, right.uid)
+ assertEquals(left.set, right.set)
+ assertEquals(left.tag, right.tag)
+ assertEquals(left.metered, right.metered)
+ assertEquals(left.roaming, right.roaming)
+ assertEquals(left.defaultNetwork, right.defaultNetwork)
+ assertEquals(left.rxBytes, right.rxBytes)
+ assertEquals(left.rxPackets, right.rxPackets)
+ assertEquals(left.txBytes, right.txBytes)
+ assertEquals(left.txPackets, right.txPackets)
+ assertEquals(left.operations, right.operations)
+ }
}
\ No newline at end of file
diff --git a/common/testutils/Android.bp b/common/testutils/Android.bp
index 1be64c1..2dda269 100644
--- a/common/testutils/Android.bp
+++ b/common/testutils/Android.bp
@@ -31,6 +31,7 @@
],
static_libs: [
"androidx.test.ext.junit",
+ "compatibility-device-util-axt",
"kotlin-reflect",
"libnanohttpd",
"net-tests-utils-host-device-common",
@@ -79,6 +80,6 @@
"host/**/*.kt",
],
libs: ["tradefed"],
- test_suites: ["device-tests", "general-tests", "cts", "mts"],
+ test_suites: ["device-tests", "general-tests", "cts", "mts-networking"],
data: [":ConnectivityChecker"],
}
diff --git a/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt b/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt
index 201bf2d..8b58e71 100644
--- a/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt
+++ b/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt
@@ -18,11 +18,15 @@
import android.os.Build
import com.android.modules.utils.build.SdkLevel
+import kotlin.test.fail
import org.junit.Assume.assumeTrue
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
+// TODO: Remove it when Build.VERSION_CODES.SC_V2 is available
+const val SC_V2 = 32
+
/**
* Returns true if the development SDK version of the device is in the provided range.
*
@@ -40,8 +44,10 @@
// For recent SDKs that still have development builds used for testing, use SdkLevel utilities
// instead of SDK_INT.
return when (minExclusive) {
- // TODO: use Build.VERSION_CODES.S when it is not CURRENT_DEVELOPMENT
- 31 -> SdkLevel.isAtLeastT()
+ // TODO: Use Build.VERSION_CODES.SC_V2 when it is available
+ SC_V2 -> SdkLevel.isAtLeastT()
+ // TODO: To use SdkLevel.isAtLeastSv2 when available
+ Build.VERSION_CODES.S -> fail("Do you expect to ignore the test until T? Use SC_V2 instead")
Build.VERSION_CODES.R -> SdkLevel.isAtLeastS()
// Development builds of SDK versions <= R are not used anymore
else -> Build.VERSION.SDK_INT > minExclusive
@@ -50,8 +56,11 @@
private fun isDevSdkUpTo(maxInclusive: Int): Boolean {
return when (maxInclusive) {
- // TODO: use Build.VERSION_CODES.S when it is not CURRENT_DEVELOPMENT
- 31 -> !SdkLevel.isAtLeastT()
+ // TODO: Use Build.VERSION_CODES.SC_V2 when it is available
+ SC_V2 -> !SdkLevel.isAtLeastT()
+ // TODO: To use SdkLevel.isAtLeastSv2 when available
+ Build.VERSION_CODES.S ->
+ fail("Do you expect to ignore the test before T? Use SC_V2 instead")
Build.VERSION_CODES.R -> !SdkLevel.isAtLeastS()
// Development builds of SDK versions <= R are not used anymore
else -> Build.VERSION.SDK_INT <= maxInclusive
diff --git a/common/testutils/devicetests/com/android/testutils/DumpTestUtils.java b/common/testutils/devicetests/com/android/testutils/DumpTestUtils.java
new file mode 100644
index 0000000..f2ad1e2
--- /dev/null
+++ b/common/testutils/devicetests/com/android/testutils/DumpTestUtils.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.testutils;
+
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.system.ErrnoException;
+import android.system.Os;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Utilities for testing output of service dumps.
+ */
+public class DumpTestUtils {
+
+ private static String dumpService(String serviceName, boolean adoptPermission, String... args)
+ throws RemoteException, InterruptedException, ErrnoException {
+ final IBinder ib = ServiceManager.getService(serviceName);
+ FileDescriptor[] pipe = Os.pipe();
+
+ // Start a thread to read the dump output, or dump might block if it fills the pipe.
+ final CountDownLatch latch = new CountDownLatch(1);
+ AtomicReference<String> output = new AtomicReference<>();
+ // Used to send exceptions back to the main thread to ensure that the test fails cleanly.
+ AtomicReference<Exception> exception = new AtomicReference<>();
+ new Thread(() -> {
+ try {
+ output.set(Streams.readFully(
+ new InputStreamReader(new FileInputStream(pipe[0]),
+ StandardCharsets.UTF_8)));
+ latch.countDown();
+ } catch (Exception e) {
+ exception.set(e);
+ latch.countDown();
+ }
+ }).start();
+
+ final int timeoutMs = 5_000;
+ final String what = "service '" + serviceName + "' with args: " + Arrays.toString(args);
+ try {
+ if (adoptPermission) {
+ runWithShellPermissionIdentity(() -> ib.dump(pipe[1], args),
+ android.Manifest.permission.DUMP);
+ } else {
+ ib.dump(pipe[1], args);
+ }
+ IoUtils.closeQuietly(pipe[1]);
+ assertTrue("Dump of " + what + " timed out after " + timeoutMs + "ms",
+ latch.await(timeoutMs, TimeUnit.MILLISECONDS));
+ } finally {
+ // Closing the fds will terminate the thread if it's blocked on read.
+ IoUtils.closeQuietly(pipe[0]);
+ if (pipe[1].valid()) IoUtils.closeQuietly(pipe[1]);
+ }
+ if (exception.get() != null) {
+ fail("Exception dumping " + what + ": " + exception.get());
+ }
+ return output.get();
+ }
+
+ /**
+ * Dumps the specified service and returns a string. Sends a dump IPC to the given service
+ * with the specified args and a pipe, then reads from the pipe in a separate thread.
+ * The current process must already have the DUMP permission.
+ *
+ * @param serviceName the service to dump.
+ * @param args the arguments to pass to the dump function.
+ * @return The dump text.
+ * @throws RemoteException dumping the service failed.
+ * @throws InterruptedException the dump timed out.
+ * @throws ErrnoException opening or closing the pipe for the dump failed.
+ */
+ public static String dumpService(String serviceName, String... args)
+ throws RemoteException, InterruptedException, ErrnoException {
+ return dumpService(serviceName, false, args);
+ }
+
+ /**
+ * Dumps the specified service and returns a string. Sends a dump IPC to the given service
+ * with the specified args and a pipe, then reads from the pipe in a separate thread.
+ * Adopts the {@code DUMP} permission via {@code adoptShellPermissionIdentity} and then releases
+ * it. This method should not be used if the caller already has the shell permission identity.
+ * TODO: when Q and R are no longer supported, use
+ * {@link android.app.UiAutomation#getAdoptedShellPermissions} to automatically acquire the
+ * shell permission if the caller does not already have it.
+ *
+ * @param serviceName the service to dump.
+ * @param args the arguments to pass to the dump function.
+ * @return The dump text.
+ * @throws RemoteException dumping the service failed.
+ * @throws InterruptedException the dump timed out.
+ * @throws ErrnoException opening or closing the pipe for the dump failed.
+ */
+ public static String dumpServiceWithShellPermission(String serviceName, String... args)
+ throws RemoteException, InterruptedException, ErrnoException {
+ return dumpService(serviceName, true, args);
+ }
+}
diff --git a/common/testutils/devicetests/com/android/testutils/TestableNetworkAgent.kt b/common/testutils/devicetests/com/android/testutils/TestableNetworkAgent.kt
index 40fb773..8dc1bc4 100644
--- a/common/testutils/devicetests/com/android/testutils/TestableNetworkAgent.kt
+++ b/common/testutils/devicetests/com/android/testutils/TestableNetworkAgent.kt
@@ -30,6 +30,7 @@
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnAddKeepalivePacketFilter
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnAutomaticReconnectDisabled
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnBandwidthUpdateRequested
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnDscpPolicyStatusUpdated
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkCreated
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkDestroyed
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkUnwanted
@@ -89,6 +90,7 @@
data class OnSignalStrengthThresholdsUpdated(val thresholds: IntArray) : CallbackEntry()
object OnNetworkCreated : CallbackEntry()
object OnNetworkDestroyed : CallbackEntry()
+ data class OnDscpPolicyStatusUpdated(val policyId: Int, val status: Int) : CallbackEntry()
data class OnRegisterQosCallback(
val callbackId: Int,
val filter: QosFilter
@@ -162,6 +164,10 @@
history.add(OnNetworkDestroyed)
}
+ override fun onDscpPolicyStatusUpdated(policyId: Int, status: Int) {
+ history.add(OnDscpPolicyStatusUpdated(policyId, status))
+ }
+
// Expects the initial validation event that always occurs immediately after registering
// a NetworkAgent whose network does not require validation (which test networks do
// not, since they lack the INTERNET capability). It always contains the default argument
@@ -197,4 +203,4 @@
"Handler didn't became idle after ${DEFAULT_TIMEOUT_MS}ms")
assertNull(history.peek())
}
-}
\ No newline at end of file
+}