Merge "Remove unused libprocessgroup dependency" into main
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index 21f36e8..aa7a244 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -807,6 +807,46 @@
*/
@SuppressLint("UnflaggedApi")
public static final class TetheringRequest implements Parcelable {
+ /**
+ * Tethering started by an explicit call to startTethering.
+ * @hide
+ */
+ public static final int REQUEST_TYPE_EXPLICIT = 0;
+
+ /**
+ * Tethering implicitly started by broadcasts (LOHS and P2P). Can never be pending.
+ * @hide
+ */
+ public static final int REQUEST_TYPE_IMPLICIT = 1;
+
+ /**
+ * Tethering started by the legacy tether() call. Can only happen on V-.
+ * @hide
+ */
+ public static final int REQUEST_TYPE_LEGACY = 2;
+
+ /**
+ * Tethering started but there was no pending request found. This may happen if Tethering is
+ * started and immediately stopped before the link layer goes up, or if we get a link layer
+ * event without a prior call to startTethering (e.g. adb shell cmd wifi start-softap).
+ * @hide
+ */
+ public static final int REQUEST_TYPE_PLACEHOLDER = 3;
+
+ /**
+ * Type of request, used to keep track of whether the request was explicitly sent by
+ * startTethering, implicitly created by broadcasts, or via legacy tether().
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "TYPE_", value = {
+ REQUEST_TYPE_EXPLICIT,
+ REQUEST_TYPE_IMPLICIT,
+ REQUEST_TYPE_LEGACY,
+ REQUEST_TYPE_PLACEHOLDER,
+ })
+ public @interface RequestType {}
+
/** A configuration set for TetheringRequest. */
private final TetheringRequestParcel mRequestParcel;
@@ -866,6 +906,7 @@
mBuilderParcel.uid = Process.INVALID_UID;
mBuilderParcel.softApConfig = null;
mBuilderParcel.interfaceName = null;
+ mBuilderParcel.requestType = REQUEST_TYPE_EXPLICIT;
}
/**
@@ -1161,6 +1202,14 @@
}
/**
+ * Get the type of the request.
+ * @hide
+ */
+ public @RequestType int getRequestType() {
+ return mRequestParcel.requestType;
+ }
+
+ /**
* String of TetheringRequest detail.
* @hide
*/
@@ -1168,6 +1217,13 @@
public String toString() {
StringJoiner sj = new StringJoiner(", ", "TetheringRequest[ ", " ]");
sj.add(typeToString(mRequestParcel.tetheringType));
+ if (mRequestParcel.requestType == REQUEST_TYPE_IMPLICIT) {
+ sj.add("IMPLICIT");
+ } else if (mRequestParcel.requestType == REQUEST_TYPE_LEGACY) {
+ sj.add("LEGACY");
+ } else if (mRequestParcel.requestType == REQUEST_TYPE_PLACEHOLDER) {
+ sj.add("PLACEHOLDER");
+ }
if (mRequestParcel.localIPv4Address != null) {
sj.add("localIpv4Address=" + mRequestParcel.localIPv4Address);
}
@@ -1217,7 +1273,8 @@
public boolean equalsIgnoreUidPackage(TetheringRequest otherRequest) {
TetheringRequestParcel parcel = getParcel();
TetheringRequestParcel otherParcel = otherRequest.getParcel();
- return parcel.tetheringType == otherParcel.tetheringType
+ return parcel.requestType == otherParcel.requestType
+ && parcel.tetheringType == otherParcel.tetheringType
&& Objects.equals(parcel.localIPv4Address, otherParcel.localIPv4Address)
&& Objects.equals(parcel.staticClientAddress, otherParcel.staticClientAddress)
&& parcel.exemptFromEntitlementCheck == otherParcel.exemptFromEntitlementCheck
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl b/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl
index 97c9b9a..9863385 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl
@@ -24,6 +24,7 @@
* @hide
*/
parcelable TetheringRequestParcel {
+ int requestType;
int tetheringType;
LinkAddress localIPv4Address;
LinkAddress staticClientAddress;
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 40b1ec0..11b14ed 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -51,6 +51,7 @@
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
+import static android.net.TetheringManager.TetheringRequest.REQUEST_TYPE_PLACEHOLDER;
import static android.net.TetheringManager.toIfaces;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
@@ -70,6 +71,8 @@
import static com.android.networkstack.tethering.metrics.TetheringStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED;
import static com.android.networkstack.tethering.metrics.TetheringStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_LEGACY_TETHER_WITH_TYPE_WIFI;
import static com.android.networkstack.tethering.metrics.TetheringStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_LEGACY_TETHER_WITH_TYPE_WIFI_P2P;
+import static com.android.networkstack.tethering.metrics.TetheringStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_LEGACY_TETHER_WITH_TYPE_WIFI_P2P_SUCCESS;
+import static com.android.networkstack.tethering.metrics.TetheringStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_LEGACY_TETHER_WITH_TYPE_WIFI_SUCCESS;
import static com.android.networkstack.tethering.util.TetheringMessageBase.BASE_MAIN_SM;
import android.app.usage.NetworkStatsManager;
@@ -144,6 +147,7 @@
import com.android.net.module.util.NetdUtils;
import com.android.net.module.util.RoutingCoordinatorManager;
import com.android.net.module.util.SharedLog;
+import com.android.net.module.util.TerribleErrorLog;
import com.android.networkstack.apishim.common.BluetoothPanShim;
import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceCallbackShim;
import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceRequestShim;
@@ -1028,6 +1032,49 @@
}
/**
+ * Create a legacy tethering request for calls to the legacy tether() API, which doesn't take an
+ * explicit request.
+ */
+ private TetheringRequest createLegacyTetheringRequest(int type, int connectivityScope) {
+ final TetheringRequest request = new TetheringRequest.Builder(type).build();
+ request.getParcel().requestType = TetheringRequest.REQUEST_TYPE_LEGACY;
+ request.getParcel().connectivityScope = connectivityScope;
+ return request;
+ }
+
+ /**
+ * Create a local-only implicit tethering request. This is used for Wifi local-only hotspot and
+ * Wifi P2P, which start tethering based on the WIFI_(AP/P2P)_STATE_CHANGED broadcasts.
+ */
+ @NonNull
+ private TetheringRequest createImplicitLocalOnlyTetheringRequest(int type) {
+ final TetheringRequest request = new TetheringRequest.Builder(type).build();
+ request.getParcel().requestType = TetheringRequest.REQUEST_TYPE_IMPLICIT;
+ request.getParcel().connectivityScope = CONNECTIVITY_SCOPE_LOCAL;
+ return request;
+ }
+
+ /**
+ * Gets the TetheringRequest that #startTethering was called with but is waiting for the link
+ * layer event to indicate the interface is available to tether.
+ * Note: There are edge cases where the pending request is absent and we must temporarily
+ * synthesize a placeholder request, such as if stopTethering was called before link layer
+ * went up, or if the link layer goes up without us poking it (e.g. adb shell cmd wifi
+ * start-softap).
+ */
+ @NonNull
+ private TetheringRequest getOrCreatePendingTetheringRequest(int type) {
+ TetheringRequest pending = mActiveTetheringRequests.get(type);
+ if (pending != null) return pending;
+
+ Log.w(TAG, "No pending TetheringRequest for type " + type + " found, creating a placeholder"
+ + " request");
+ TetheringRequest placeholder = new TetheringRequest.Builder(type).build();
+ placeholder.getParcel().requestType = REQUEST_TYPE_PLACEHOLDER;
+ return placeholder;
+ }
+
+ /**
* Legacy tether API that starts tethering with CONNECTIVITY_SCOPE_GLOBAL on the given iface.
*
* This API relies on the IpServer having been started for the interface by
@@ -1040,7 +1087,7 @@
* WIFI_(AP/P2P_STATE_CHANGED broadcasts, which makes this API redundant for those types unless
* those broadcasts are disabled by OEM.
*/
- void tether(String iface, int requestedState, final IIntResultListener listener) {
+ void legacyTether(String iface, int requestedState, final IIntResultListener listener) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.VANILLA_ICE_CREAM) {
// After V, the TetheringManager and ConnectivityManager tether and untether methods
// throw UnsupportedOperationException, so this cannot happen in normal use. Ensure
@@ -1049,30 +1096,43 @@
return;
}
mHandler.post(() -> {
+ int result = tetherInternal(iface, requestedState);
switch (ifaceNameToType(iface)) {
case TETHERING_WIFI:
- TetheringStatsLog.write(
+ TerribleErrorLog.logTerribleError(TetheringStatsLog::write,
+ "Legacy tether API called on Wifi iface " + iface,
CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED,
- CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_LEGACY_TETHER_WITH_TYPE_WIFI
- );
+ CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_LEGACY_TETHER_WITH_TYPE_WIFI);
+ if (result == TETHER_ERROR_NO_ERROR) {
+ TerribleErrorLog.logTerribleError(TetheringStatsLog::write,
+ "Legacy tether API succeeded on Wifi iface " + iface,
+ CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED,
+ CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_LEGACY_TETHER_WITH_TYPE_WIFI_SUCCESS);
+ }
break;
case TETHERING_WIFI_P2P:
- TetheringStatsLog.write(
+ TerribleErrorLog.logTerribleError(TetheringStatsLog::write,
+ "Legacy tether API called on Wifi P2P iface " + iface,
CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED,
- CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_LEGACY_TETHER_WITH_TYPE_WIFI_P2P
- );
+ CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_LEGACY_TETHER_WITH_TYPE_WIFI_P2P);
+ if (result == TETHER_ERROR_NO_ERROR) {
+ TerribleErrorLog.logTerribleError(TetheringStatsLog::write,
+ "Legacy tether API succeeded on Wifi P2P iface " + iface,
+ CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED,
+ CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_LEGACY_TETHER_WITH_TYPE_WIFI_P2P_SUCCESS);
+ }
break;
default:
// Do nothing
break;
}
try {
- listener.onResult(tether(iface, requestedState));
+ listener.onResult(result);
} catch (RemoteException e) { }
});
}
- private int tether(String iface, int requestedState) {
+ private int tetherInternal(String iface, int requestedState) {
if (DBG) Log.d(TAG, "Tethering " + iface);
TetherState tetherState = mTetherStates.get(iface);
if (tetherState == null) {
@@ -1098,7 +1158,7 @@
return TETHER_ERROR_NO_ERROR;
}
- void untether(String iface, final IIntResultListener listener) {
+ void legacyUntether(String iface, final IIntResultListener listener) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.VANILLA_ICE_CREAM) {
// After V, the TetheringManager and ConnectivityManager tether and untether methods
// throw UnsupportedOperationException, so this cannot happen in normal use. Ensure
@@ -1108,13 +1168,13 @@
}
mHandler.post(() -> {
try {
- listener.onResult(untether(iface));
+ listener.onResult(legacyUntetherInternal(iface));
} catch (RemoteException e) {
}
});
}
- int untether(String iface) {
+ int legacyUntetherInternal(String iface) {
if (DBG) Log.d(TAG, "Untethering " + iface);
TetherState tetherState = mTetherStates.get(iface);
if (tetherState == null) {
@@ -1129,7 +1189,7 @@
return TETHER_ERROR_NO_ERROR;
}
- void untetherAll() {
+ void stopAllTethering() {
stopTethering(TETHERING_WIFI);
stopTethering(TETHERING_WIFI_P2P);
stopTethering(TETHERING_USB);
@@ -1299,7 +1359,7 @@
mLog.log("OBSERVED data saver changed");
handleDataSaverChanged();
} else if (action.equals(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING)) {
- untetherAll();
+ stopAllTethering();
}
}
@@ -1463,7 +1523,7 @@
mDataSaverEnabled = isDataSaverEnabled;
if (mDataSaverEnabled) {
- untetherAll();
+ stopAllTethering();
}
}
}
@@ -1522,7 +1582,7 @@
mNotificationUpdater.notifyTetheringDisabledByRestriction();
// Untether from all downstreams since tethering is disallowed.
- mTethering.untetherAll();
+ mTethering.stopAllTethering();
}
return true;
@@ -1537,7 +1597,7 @@
private void enableIpServing(int tetheringType, String ifname, int ipServingMode,
boolean isNcm) {
ensureIpServerStarted(ifname, tetheringType, isNcm);
- if (tether(ifname, ipServingMode) != TETHER_ERROR_NO_ERROR) {
+ if (tetherInternal(ifname, ipServingMode) != TETHER_ERROR_NO_ERROR) {
Log.e(TAG, "unable start tethering on iface " + ifname);
}
}
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
index 0c44a38..153b0f7 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringService.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
@@ -111,7 +111,7 @@
IIntResultListener listener) {
if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
- mTethering.tether(iface, IpServer.STATE_TETHERED, listener);
+ mTethering.legacyTether(iface, IpServer.STATE_TETHERED, listener);
}
@Override
@@ -119,7 +119,7 @@
IIntResultListener listener) {
if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
- mTethering.untether(iface, listener);
+ mTethering.legacyUntether(iface, listener);
}
@Override
@@ -200,7 +200,7 @@
if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
try {
- mTethering.untetherAll();
+ mTethering.stopAllTethering();
listener.onResult(TETHER_ERROR_NO_ERROR);
} catch (RemoteException e) { }
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
index cc80251..da9b68e 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
@@ -219,7 +219,7 @@
mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
- verify(mTethering).tether(TEST_IFACE_NAME, IpServer.STATE_TETHERED, result);
+ verify(mTethering).legacyTether(TEST_IFACE_NAME, IpServer.STATE_TETHERED, result);
}
@Test
@@ -267,7 +267,7 @@
result);
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
- verify(mTethering).untether(eq(TEST_IFACE_NAME), eq(result));
+ verify(mTethering).legacyUntether(eq(TEST_IFACE_NAME), eq(result));
}
@Test
@@ -661,7 +661,7 @@
mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
- verify(mTethering).untetherAll();
+ verify(mTethering).stopAllTethering();
result.assertResult(TETHER_ERROR_NO_ERROR);
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 97758cf..e50a7fd 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -2098,7 +2098,7 @@
verify(mNotificationUpdater, times(expectedInteractionsWithShowNotification))
.notifyTetheringDisabledByRestriction();
- verify(mockTethering, times(expectedInteractionsWithShowNotification)).untetherAll();
+ verify(mockTethering, times(expectedInteractionsWithShowNotification)).stopAllTethering();
}
@Test
@@ -3426,7 +3426,7 @@
mTethering.interfaceStatusChanged(TEST_BT_IFNAME, false);
mTethering.interfaceStatusChanged(TEST_BT_IFNAME, true);
final ResultListener tetherResult = new ResultListener(TETHER_ERROR_NO_ERROR);
- mTethering.tether(TEST_BT_IFNAME, IpServer.STATE_TETHERED, tetherResult);
+ mTethering.legacyTether(TEST_BT_IFNAME, IpServer.STATE_TETHERED, tetherResult);
mLooper.dispatchAll();
tetherResult.assertHasResult();
@@ -3446,7 +3446,7 @@
mTethering.stopTethering(TETHERING_BLUETOOTH);
mLooper.dispatchAll();
final ResultListener untetherResult = new ResultListener(TETHER_ERROR_NO_ERROR);
- mTethering.untether(TEST_BT_IFNAME, untetherResult);
+ mTethering.legacyUntether(TEST_BT_IFNAME, untetherResult);
mLooper.dispatchAll();
untetherResult.assertHasResult();
verifySetBluetoothTethering(false /* enable */, false /* bindToPanService */);
@@ -3476,7 +3476,7 @@
mTethering.interfaceStatusChanged(TEST_BT_IFNAME, false);
mTethering.interfaceStatusChanged(TEST_BT_IFNAME, true);
final ResultListener tetherResult = new ResultListener(TETHER_ERROR_NO_ERROR);
- mTethering.tether(TEST_BT_IFNAME, IpServer.STATE_TETHERED, tetherResult);
+ mTethering.legacyTether(TEST_BT_IFNAME, IpServer.STATE_TETHERED, tetherResult);
mLooper.dispatchAll();
tetherResult.assertHasResult();
}
diff --git a/bpf/headers/include/bpf/BpfMap.h b/bpf/headers/include/bpf/BpfMap.h
index 1037beb..576cca6 100644
--- a/bpf/headers/include/bpf/BpfMap.h
+++ b/bpf/headers/include/bpf/BpfMap.h
@@ -26,6 +26,7 @@
#include "BpfSyscallWrappers.h"
#include "bpf/BpfUtils.h"
+#include <cstdio>
#include <functional>
namespace android {
@@ -35,6 +36,30 @@
using base::unique_fd;
using std::function;
+#ifdef BPF_MAP_MAKE_VISIBLE_FOR_TESTING
+#undef BPFMAP_VERBOSE_ABORT
+#define BPFMAP_VERBOSE_ABORT
+#endif
+
+[[noreturn]] __attribute__((__format__(__printf__, 2, 3))) static inline
+void Abort(int __unused error, const char* __unused fmt, ...) {
+#ifdef BPFMAP_VERBOSE_ABORT
+ va_list va;
+ va_start(va, fmt);
+
+ fflush(stdout);
+ vfprintf(stderr, fmt, va);
+ if (error) fprintf(stderr, "; errno=%d [%s]", error, strerror(error));
+ putc('\n', stderr);
+ fflush(stderr);
+
+ va_end(va);
+#endif
+
+ abort();
+}
+
+
// This is a class wrapper for eBPF maps. The eBPF map is a special in-kernel
// data structure that stores data in <Key, Value> pairs. It can be read/write
// from userspace by passing syscalls with the map file descriptor. This class
@@ -60,14 +85,21 @@
protected:
void abortOnMismatch(bool writable) const {
- if (!mMapFd.ok()) abort();
+ if (!mMapFd.ok()) Abort(errno, "mMapFd %d is not valid", mMapFd.get());
if (isAtLeastKernelVersion(4, 14, 0)) {
int flags = bpfGetFdMapFlags(mMapFd);
- if (flags < 0) abort();
- if (flags & BPF_F_WRONLY) abort();
- if (writable && (flags & BPF_F_RDONLY)) abort();
- if (bpfGetFdKeySize(mMapFd) != sizeof(Key)) abort();
- if (bpfGetFdValueSize(mMapFd) != sizeof(Value)) abort();
+ if (flags < 0) Abort(errno, "bpfGetFdMapFlags fail: flags=%d", flags);
+ if (flags & BPF_F_WRONLY) Abort(0, "map is write-only (flags=0x%X)", flags);
+ if (writable && (flags & BPF_F_RDONLY))
+ Abort(0, "writable map is actually read-only (flags=0x%X)", flags);
+ int keySize = bpfGetFdKeySize(mMapFd);
+ if (keySize != sizeof(Key))
+ Abort(errno, "map key size mismatch (expected=%zu, actual=%d)",
+ sizeof(Key), keySize);
+ int valueSize = bpfGetFdValueSize(mMapFd);
+ if (valueSize != sizeof(Value))
+ Abort(errno, "map value size mismatch (expected=%zu, actual=%d)",
+ sizeof(Value), valueSize);
}
}
@@ -278,8 +310,8 @@
[[clang::reinitializes]] Result<void> resetMap(bpf_map_type map_type,
uint32_t max_entries,
uint32_t map_flags = 0) {
- if (map_flags & BPF_F_WRONLY) abort();
- if (map_flags & BPF_F_RDONLY) abort();
+ if (map_flags & BPF_F_WRONLY) Abort(0, "map_flags is write-only");
+ if (map_flags & BPF_F_RDONLY) Abort(0, "map_flags is read-only");
mMapFd.reset(createMap(map_type, sizeof(Key), sizeof(Value), max_entries,
map_flags));
if (!mMapFd.ok()) return ErrnoErrorf("BpfMap::resetMap() failed");
diff --git a/bpf/headers/include/bpf/BpfUtils.h b/bpf/headers/include/bpf/BpfUtils.h
index 9dd5822..9e8b2c7 100644
--- a/bpf/headers/include/bpf/BpfUtils.h
+++ b/bpf/headers/include/bpf/BpfUtils.h
@@ -63,9 +63,9 @@
// 4.9 kernels. The kernel code of socket release on pf_key socket will
// explicitly call synchronize_rcu() which is exactly what we need.
//
- // Linux 4.14/4.19/5.4/5.10/5.15/6.1 (and 6.3-rc5) still have this same behaviour.
+ // Linux 4.14/4.19/5.4/5.10/5.15/6.1/6.6/6.12 (& 6.13) have this behaviour.
// see net/key/af_key.c: pfkey_release() -> synchronize_rcu()
- // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/key/af_key.c?h=v6.3-rc5#n185
+ // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/key/af_key.c?h=v6.13#n185
const int pfSocket = socket(AF_KEY, SOCK_RAW | SOCK_CLOEXEC, PF_KEY_V2);
if (pfSocket < 0) {
diff --git a/bpf/headers/include/bpf_helpers.h b/bpf/headers/include/bpf_helpers.h
index 0bd3421..67de633 100644
--- a/bpf/headers/include/bpf_helpers.h
+++ b/bpf/headers/include/bpf_helpers.h
@@ -122,28 +122,46 @@
*/
#define CRITICAL(REASON) char _critical[] SECTION("critical") = (REASON)
-/*
- * Helper functions called from eBPF programs written in C. These are
- * implemented in the kernel sources.
- */
+// Helpers for writing kernel version specific bpf programs
struct kver_uint { unsigned int kver; };
#define KVER_(v) ((struct kver_uint){ .kver = (v) })
#define KVER(a, b, c) KVER_(((a) << 24) + ((b) << 16) + (c))
#define KVER_NONE KVER_(0)
+#define KVER_4_9 KVER(4, 9, 0)
#define KVER_4_14 KVER(4, 14, 0)
#define KVER_4_19 KVER(4, 19, 0)
#define KVER_5_4 KVER(5, 4, 0)
-#define KVER_5_8 KVER(5, 8, 0)
-#define KVER_5_9 KVER(5, 9, 0)
#define KVER_5_10 KVER(5, 10, 0)
#define KVER_5_15 KVER(5, 15, 0)
#define KVER_6_1 KVER(6, 1, 0)
#define KVER_6_6 KVER(6, 6, 0)
+#define KVER_6_12 KVER(6, 12, 0)
#define KVER_INF KVER_(0xFFFFFFFFu)
#define KVER_IS_AT_LEAST(kver, a, b, c) ((kver).kver >= KVER(a, b, c).kver)
+// Helpers for writing sdk level specific bpf programs
+//
+// Note: we choose to follow sdk api level values, but there is no real need for this:
+// These just need to be monotonically increasing. We could also use values ten or even
+// a hundred times larger to leave room for quarters or months. We may also just use
+// dates or something (2502 or 202506 for 25Q2) or even the mainline bpfloader version...
+// For now this easily suffices for our use case.
+
+struct sdk_level_uint { unsigned int sdk_level; };
+#define SDK_LEVEL_(v) ((struct sdk_level_uint){ .sdk_level = (v) })
+#define SDK_LEVEL_NONE SDK_LEVEL_(0)
+#define SDK_LEVEL_S SDK_LEVEL_(31) // Android 12
+#define SDK_LEVEL_Sv2 SDK_LEVEL_(32) // Android 12L
+#define SDK_LEVEL_T SDK_LEVEL_(33) // Android 13
+#define SDK_LEVEL_U SDK_LEVEL_(34) // Android 14
+#define SDK_LEVEL_V SDK_LEVEL_(35) // Android 15
+#define SDK_LEVEL_24Q3 SDK_LEVEL_V
+#define SDK_LEVEL_25Q2 SDK_LEVEL_(36) // Android 16
+
+#define SDK_LEVEL_IS_AT_LEAST(lvl, v) ((lvl).sdk_level >= (SDK_LEVEL_##v).sdk_level)
+
/*
* BPFFS (ie. /sys/fs/bpf) labelling is as follows:
* subdirectory selinux context mainline usecase / usable by
@@ -168,6 +186,11 @@
* See cs/p:aosp-master%20-file:prebuilts/%20file:genfs_contexts%20"genfscon%20bpf"
*/
+/*
+ * Helper functions called from eBPF programs written in C. These are
+ * implemented in the kernel sources.
+ */
+
/* generic functions */
/*
@@ -231,16 +254,24 @@
(ignore_userdebug).ignore_on_userdebug), \
"bpfloader min version must be >= 0.33 in order to use ignored_on");
+#define ABSOLUTE(x) ((x) < 0 ? -(x) : (x))
+
+#define DEFAULT_BPF_MAP_FLAGS(type, num_entries, mapflags) \
+ ( (mapflags) | \
+ ((num_entries) < 0 ? BPF_F_NO_PREALLOC : 0) | \
+ (type == BPF_MAP_TYPE_LPM_TRIE ? BPF_F_NO_PREALLOC : 0) \
+ )
+
#define DEFINE_BPF_MAP_BASE(the_map, TYPE, keysize, valuesize, num_entries, \
usr, grp, md, selinux, pindir, share, minkver, \
maxkver, minloader, maxloader, ignore_eng, \
- ignore_user, ignore_userdebug) \
+ ignore_user, ignore_userdebug, mapflags) \
const struct bpf_map_def SECTION("maps") the_map = { \
.type = BPF_MAP_TYPE_##TYPE, \
.key_size = (keysize), \
.value_size = (valuesize), \
- .max_entries = (num_entries), \
- .map_flags = 0, \
+ .max_entries = ABSOLUTE(num_entries), \
+ .map_flags = DEFAULT_BPF_MAP_FLAGS(BPF_MAP_TYPE_##TYPE, num_entries, mapflags), \
.uid = (usr), \
.gid = (grp), \
.mode = (md), \
@@ -260,16 +291,17 @@
// Type safe macro to declare a ring buffer and related output functions.
// Compatibility:
// * BPF ring buffers are only available kernels 5.8 and above. Any program
-// accessing the ring buffer should set a program level min_kver >= 5.8.
-// * The definition below sets a map min_kver of 5.8 which requires targeting
+// accessing the ring buffer should set a program level min_kver >= 5.10,
+// since 5.10 is the next LTS version.
+// * The definition below sets a map min_kver of 5.10 which requires targeting
// a BPFLOADER_MIN_VER >= BPFLOADER_S_VERSION.
#define DEFINE_BPF_RINGBUF_EXT(the_map, ValueType, size_bytes, usr, grp, md, \
selinux, pindir, share, min_loader, max_loader, \
ignore_eng, ignore_user, ignore_userdebug) \
DEFINE_BPF_MAP_BASE(the_map, RINGBUF, 0, 0, size_bytes, usr, grp, md, \
- selinux, pindir, share, KVER_5_8, KVER_INF, \
+ selinux, pindir, share, KVER_5_10, KVER_INF, \
min_loader, max_loader, ignore_eng, ignore_user, \
- ignore_userdebug); \
+ ignore_userdebug, 0); \
\
_Static_assert((size_bytes) >= 4096, "min 4 kiB ringbuffer size"); \
_Static_assert((size_bytes) <= 0x10000000, "max 256 MiB ringbuffer size"); \
@@ -317,11 +349,11 @@
/* type safe macro to declare a map and related accessor functions */
#define DEFINE_BPF_MAP_EXT(the_map, TYPE, KeyType, ValueType, num_entries, usr, grp, md, \
selinux, pindir, share, min_loader, max_loader, ignore_eng, \
- ignore_user, ignore_userdebug) \
+ ignore_user, ignore_userdebug, mapFlags) \
DEFINE_BPF_MAP_BASE(the_map, TYPE, sizeof(KeyType), sizeof(ValueType), \
num_entries, usr, grp, md, selinux, pindir, share, \
KVER_NONE, KVER_INF, min_loader, max_loader, \
- ignore_eng, ignore_user, ignore_userdebug); \
+ ignore_eng, ignore_user, ignore_userdebug, mapFlags); \
BPF_MAP_ASSERT_OK(BPF_MAP_TYPE_##TYPE, (num_entries), (md)); \
_Static_assert(sizeof(KeyType) < 1024, "aosp/2370288 requires < 1024 byte keys"); \
_Static_assert(sizeof(ValueType) < 65536, "aosp/2370288 requires < 65536 byte values"); \
@@ -359,13 +391,13 @@
#define DEFINE_BPF_MAP_KERNEL_INTERNAL(the_map, TYPE, KeyType, ValueType, num_entries) \
DEFINE_BPF_MAP_EXT(the_map, TYPE, KeyType, ValueType, num_entries, AID_ROOT, AID_ROOT, \
0000, "fs_bpf_loader", "", PRIVATE, BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, \
- LOAD_ON_ENG, LOAD_ON_USER, LOAD_ON_USERDEBUG)
+ LOAD_ON_ENG, LOAD_ON_USER, LOAD_ON_USERDEBUG, 0)
#define DEFINE_BPF_MAP_UGM(the_map, TYPE, KeyType, ValueType, num_entries, usr, grp, md) \
DEFINE_BPF_MAP_EXT(the_map, TYPE, KeyType, ValueType, num_entries, usr, grp, md, \
DEFAULT_BPF_MAP_SELINUX_CONTEXT, DEFAULT_BPF_MAP_PIN_SUBDIR, \
PRIVATE, BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, \
- LOAD_ON_ENG, LOAD_ON_USER, LOAD_ON_USERDEBUG)
+ LOAD_ON_ENG, LOAD_ON_USER, LOAD_ON_USERDEBUG, 0)
#define DEFINE_BPF_MAP(the_map, TYPE, KeyType, ValueType, num_entries) \
DEFINE_BPF_MAP_UGM(the_map, TYPE, KeyType, ValueType, num_entries, \
diff --git a/bpf/headers/include/bpf_map_def.h b/bpf/headers/include/bpf_map_def.h
index 2d6736c..d67da48 100644
--- a/bpf/headers/include/bpf_map_def.h
+++ b/bpf/headers/include/bpf_map_def.h
@@ -94,6 +94,10 @@
_Static_assert(_Alignof(enum bpf_map_type) == 4, "_Alignof enum bpf_map_type != 4");
// Linux kernel requires sizeof(int) == 4, sizeof(void*) == sizeof(long), sizeof(long long) == 8
+_Static_assert(sizeof(int) == 4, "sizeof int != 4");
+_Static_assert(__alignof__(int) == 4, "__alignof__ int != 4");
+_Static_assert(_Alignof(int) == 4, "_Alignof int != 4");
+
_Static_assert(sizeof(unsigned int) == 4, "sizeof unsigned int != 4");
_Static_assert(__alignof__(unsigned int) == 4, "__alignof__ unsigned int != 4");
_Static_assert(_Alignof(unsigned int) == 4, "_Alignof unsigned int != 4");
@@ -155,7 +159,7 @@
enum bpf_map_type type;
unsigned int key_size;
unsigned int value_size;
- unsigned int max_entries;
+ int max_entries; // negative means BPF_F_NO_PREALLOC, but *might* not work with S
unsigned int map_flags;
// The following are not supported by the Android bpfloader:
diff --git a/bpf/loader/NetBpfLoad.cpp b/bpf/loader/NetBpfLoad.cpp
index 038786c..53c7d49 100644
--- a/bpf/loader/NetBpfLoad.cpp
+++ b/bpf/loader/NetBpfLoad.cpp
@@ -616,9 +616,6 @@
if (type == BPF_MAP_TYPE_DEVMAP || type == BPF_MAP_TYPE_DEVMAP_HASH)
desired_map_flags |= BPF_F_RDONLY_PROG;
- if (type == BPF_MAP_TYPE_LPM_TRIE)
- desired_map_flags |= BPF_F_NO_PREALLOC;
-
// The .h file enforces that this is a power of two, and page size will
// also always be a power of two, so this logic is actually enough to
// force it to be a multiple of the page size, as required by the kernel.
@@ -794,7 +791,7 @@
.key_size = md[i].key_size,
.value_size = md[i].value_size,
.max_entries = max_entries,
- .map_flags = md[i].map_flags | (type == BPF_MAP_TYPE_LPM_TRIE ? BPF_F_NO_PREALLOC : 0),
+ .map_flags = md[i].map_flags,
};
if (isAtLeastKernelVersion(4, 15, 0))
strlcpy(req.map_name, mapNames[i].c_str(), sizeof(req.map_name));
diff --git a/bpf/progs/netd.c b/bpf/progs/netd.c
index ed0eed5..aab9c26 100644
--- a/bpf/progs/netd.c
+++ b/bpf/progs/netd.c
@@ -25,10 +25,6 @@
static const int PASS = 1;
static const int DROP_UNLESS_DNS = 2; // internal to our program
-// Used for 'bool enable_tracing'
-static const bool TRACE_ON = true;
-static const bool TRACE_OFF = false;
-
// offsetof(struct iphdr, ihl) -- but that's a bitfield
#define IPPROTO_IHL_OFF 0
@@ -46,14 +42,14 @@
DEFINE_BPF_MAP_EXT(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, \
AID_ROOT, AID_NET_BW_ACCT, 0060, "fs_bpf_net_shared", "", \
PRIVATE, BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, \
- LOAD_ON_ENG, LOAD_ON_USER, LOAD_ON_USERDEBUG)
+ LOAD_ON_ENG, LOAD_ON_USER, LOAD_ON_USERDEBUG, 0)
// For maps netd only needs read only access to
#define DEFINE_BPF_MAP_RO_NETD(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries) \
DEFINE_BPF_MAP_EXT(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, \
AID_ROOT, AID_NET_BW_ACCT, 0460, "fs_bpf_netd_readonly", "", \
PRIVATE, BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, \
- LOAD_ON_ENG, LOAD_ON_USER, LOAD_ON_USERDEBUG)
+ LOAD_ON_ENG, LOAD_ON_USER, LOAD_ON_USERDEBUG, 0)
// For maps netd needs to be able to read and write
#define DEFINE_BPF_MAP_RW_NETD(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries) \
@@ -92,7 +88,7 @@
DEFINE_BPF_MAP_EXT(packet_trace_enabled_map, ARRAY, uint32_t, bool, 1,
AID_ROOT, AID_SYSTEM, 0060, "fs_bpf_net_shared", "", PRIVATE,
BPFLOADER_MAINLINE_U_VERSION, BPFLOADER_MAX_VER, LOAD_ON_ENG,
- LOAD_ON_USER, LOAD_ON_USERDEBUG)
+ LOAD_ON_USER, LOAD_ON_USERDEBUG, 0)
// A ring buffer on which packet information is pushed.
DEFINE_BPF_RINGBUF_EXT(packet_trace_ringbuf, PacketTrace, PACKET_TRACE_BUF_SIZE,
@@ -103,6 +99,17 @@
DEFINE_BPF_MAP_RO_NETD(data_saver_enabled_map, ARRAY, uint32_t, bool,
DATA_SAVER_ENABLED_MAP_SIZE)
+DEFINE_BPF_MAP_EXT(local_net_access_map, LPM_TRIE, LocalNetAccessKey, bool, 1000,
+ AID_ROOT, AID_NET_BW_ACCT, 0060, "fs_bpf_net_shared", "", PRIVATE,
+ BPFLOADER_MAINLINE_25Q2_VERSION, BPFLOADER_MAX_VER, LOAD_ON_ENG, LOAD_ON_USER,
+ LOAD_ON_USERDEBUG, 0)
+
+// not preallocated
+DEFINE_BPF_MAP_EXT(local_net_blocked_uid_map, HASH, uint32_t, bool, -1000,
+ AID_ROOT, AID_NET_BW_ACCT, 0060, "fs_bpf_net_shared", "", PRIVATE,
+ BPFLOADER_MAINLINE_25Q2_VERSION, BPFLOADER_MAX_VER, LOAD_ON_ENG, LOAD_ON_USER,
+ LOAD_ON_USERDEBUG, 0)
+
// iptables xt_bpf programs need to be usable by both netd and netutils_wrappers
// selinux contexts, because even non-xt_bpf iptables mutations are implemented as
// a full table dump, followed by an update in userspace, and then a reload into the kernel,
@@ -110,31 +117,34 @@
// program (see XT_BPF_MODE_PATH_PINNED) and then the iptables binary (or rather
// the kernel acting on behalf of it) must be able to retrieve the pinned program
// for the reload to succeed
-#define DEFINE_XTBPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \
- DEFINE_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog)
+#define DEFINE_XTBPF_PROG(SECTION_NAME, the_prog) \
+ DEFINE_BPF_PROG(SECTION_NAME, AID_ROOT, AID_NET_ADMIN, the_prog)
// programs that need to be usable by netd, but not by netutils_wrappers
// (this is because these are currently attached by the mainline provided libnetd_updatable .so
// which is loaded into netd and thus runs as netd uid/gid/selinux context)
-#define DEFINE_NETD_BPF_PROG_KVER_RANGE(SECTION_NAME, prog_uid, prog_gid, the_prog, minKV, maxKV) \
- DEFINE_BPF_PROG_EXT(SECTION_NAME, prog_uid, prog_gid, the_prog, \
- minKV, maxKV, BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, MANDATORY, \
+#define DEFINE_NETD_BPF_PROG_RANGES(SECTION_NAME, the_prog, minKV, maxKV, min_loader, max_loader) \
+ DEFINE_BPF_PROG_EXT(SECTION_NAME, AID_ROOT, AID_ROOT, the_prog, \
+ minKV, maxKV, min_loader, max_loader, MANDATORY, \
"fs_bpf_netd_readonly", "", LOAD_ON_ENG, LOAD_ON_USER, LOAD_ON_USERDEBUG)
-#define DEFINE_NETD_BPF_PROG_KVER(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv) \
- DEFINE_NETD_BPF_PROG_KVER_RANGE(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, KVER_INF)
+#define DEFINE_NETD_BPF_PROG_KVER_RANGE(SECTION_NAME, the_prog, minKV, maxKV) \
+ DEFINE_NETD_BPF_PROG_RANGES(SECTION_NAME, the_prog, minKV, maxKV, BPFLOADER_MIN_VER, BPFLOADER_MAX_VER)
-#define DEFINE_NETD_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \
- DEFINE_NETD_BPF_PROG_KVER(SECTION_NAME, prog_uid, prog_gid, the_prog, KVER_NONE)
+#define DEFINE_NETD_BPF_PROG_KVER(SECTION_NAME, the_prog, min_kv) \
+ DEFINE_NETD_BPF_PROG_KVER_RANGE(SECTION_NAME, the_prog, min_kv, KVER_INF)
-#define DEFINE_NETD_V_BPF_PROG_KVER(SECTION_NAME, prog_uid, prog_gid, the_prog, minKV) \
- DEFINE_BPF_PROG_EXT(SECTION_NAME, prog_uid, prog_gid, the_prog, minKV, \
+#define DEFINE_NETD_BPF_PROG(SECTION_NAME, the_prog) \
+ DEFINE_NETD_BPF_PROG_KVER(SECTION_NAME, the_prog, KVER_NONE)
+
+#define DEFINE_NETD_V_BPF_PROG_KVER(SECTION_NAME, the_prog, minKV) \
+ DEFINE_BPF_PROG_EXT(SECTION_NAME, AID_ROOT, AID_ROOT, the_prog, minKV, \
KVER_INF, BPFLOADER_MAINLINE_V_VERSION, BPFLOADER_MAX_VER, MANDATORY, \
"fs_bpf_netd_readonly", "", LOAD_ON_ENG, LOAD_ON_USER, LOAD_ON_USERDEBUG)
// programs that only need to be usable by the system server
-#define DEFINE_SYS_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \
- DEFINE_BPF_PROG_EXT(SECTION_NAME, prog_uid, prog_gid, the_prog, KVER_NONE, KVER_INF, \
+#define DEFINE_SYS_BPF_PROG(SECTION_NAME, the_prog) \
+ DEFINE_BPF_PROG_EXT(SECTION_NAME, AID_ROOT, AID_NET_ADMIN, the_prog, KVER_NONE, KVER_INF, \
BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, MANDATORY, \
"fs_bpf_net_shared", "", LOAD_ON_ENG, LOAD_ON_USER, LOAD_ON_USERDEBUG)
@@ -231,11 +241,70 @@
: bpf_skb_load_bytes(skb, L3_off, to, len);
}
+// False iff arguments are found with longest prefix match lookup and disallowed.
+static inline __always_inline bool is_local_net_access_allowed(const uint32_t if_index,
+ const struct in6_addr* remote_ip6, const uint16_t protocol, const __be16 remote_port) {
+ LocalNetAccessKey query_key = {
+ .lpm_bitlen = 8 * (sizeof(if_index) + sizeof(*remote_ip6) + sizeof(protocol)
+ + sizeof(remote_port)),
+ .if_index = if_index,
+ .remote_ip6 = *remote_ip6,
+ .protocol = protocol,
+ .remote_port = remote_port
+ };
+ bool* v = bpf_local_net_access_map_lookup_elem(&query_key);
+ return v ? *v : true;
+}
+
+static __always_inline inline bool should_block_local_network_packets(struct __sk_buff *skb,
+ const uint32_t uid, const struct egress_bool egress,
+ const struct kver_uint kver) {
+ if (is_system_uid(uid)) return false;
+
+ bool* block_local_net = bpf_local_net_blocked_uid_map_lookup_elem(&uid);
+ if (!block_local_net) return false; // uid not found in map
+ if (!*block_local_net) return false; // lookup returned 'bool false'
+
+ struct in6_addr remote_ip6;
+ uint8_t ip_proto;
+ uint8_t L4_off;
+ if (skb->protocol == htons(ETH_P_IP)) {
+ int remote_ip_ofs = egress.egress ? IP4_OFFSET(daddr) : IP4_OFFSET(saddr);
+ remote_ip6.s6_addr32[0] = 0;
+ remote_ip6.s6_addr32[1] = 0;
+ remote_ip6.s6_addr32[2] = htonl(0xFFFF);
+ (void)bpf_skb_load_bytes_net(skb, remote_ip_ofs, &remote_ip6.s6_addr32[3], 4, kver);
+ (void)bpf_skb_load_bytes_net(skb, IP4_OFFSET(protocol), &ip_proto, sizeof(ip_proto), kver);
+ uint8_t ihl;
+ (void)bpf_skb_load_bytes_net(skb, IPPROTO_IHL_OFF, &ihl, sizeof(ihl), kver);
+ L4_off = (ihl & 0x0F) * 4; // IHL calculation.
+ } else if (skb->protocol == htons(ETH_P_IPV6)) {
+ int remote_ip_ofs = egress.egress ? IP6_OFFSET(daddr) : IP6_OFFSET(saddr);
+ (void)bpf_skb_load_bytes_net(skb, remote_ip_ofs, &remote_ip6, sizeof(remote_ip6), kver);
+ (void)bpf_skb_load_bytes_net(skb, IP6_OFFSET(nexthdr), &ip_proto, sizeof(ip_proto), kver);
+ L4_off = sizeof(struct ipv6hdr);
+ } else {
+ return false;
+ }
+
+ __be16 remote_port = 0;
+ switch (ip_proto) {
+ case IPPROTO_TCP:
+ case IPPROTO_DCCP:
+ case IPPROTO_UDP:
+ case IPPROTO_UDPLITE:
+ case IPPROTO_SCTP:
+ (void)bpf_skb_load_bytes_net(skb, L4_off + (egress.egress ? 2 : 0), &remote_port, sizeof(remote_port), kver);
+ break;
+ }
+
+ return !is_local_net_access_allowed(skb->ifindex, &remote_ip6, ip_proto, remote_port);
+}
+
static __always_inline inline void do_packet_tracing(
const struct __sk_buff* const skb, const struct egress_bool egress, const uint32_t uid,
- const uint32_t tag, const bool enable_tracing, const struct kver_uint kver) {
- if (!enable_tracing) return;
- if (!KVER_IS_AT_LEAST(kver, 5, 8, 0)) return;
+ const uint32_t tag, const struct kver_uint kver) {
+ if (!KVER_IS_AT_LEAST(kver, 5, 10, 0)) return;
uint32_t mapKey = 0;
bool* traceConfig = bpf_packet_trace_enabled_map_lookup_elem(&mapKey);
@@ -393,7 +462,8 @@
static __always_inline inline int bpf_owner_match(struct __sk_buff* skb, uint32_t uid,
const struct egress_bool egress,
- const struct kver_uint kver) {
+ const struct kver_uint kver,
+ const struct sdk_level_uint lvl) {
if (is_system_uid(uid)) return PASS;
if (skip_owner_match(skb, egress, kver)) return PASS;
@@ -423,6 +493,11 @@
return DROP_UNLESS_DNS;
}
}
+
+ if (SDK_LEVEL_IS_AT_LEAST(lvl, 25Q2) && skb->ifindex == 1) {
+ // TODO: sdksandbox localhost restrictions
+ }
+
return PASS;
}
@@ -440,8 +515,8 @@
static __always_inline inline int bpf_traffic_account(struct __sk_buff* skb,
const struct egress_bool egress,
- const bool enable_tracing,
- const struct kver_uint kver) {
+ const struct kver_uint kver,
+ const struct sdk_level_uint lvl) {
// sock_uid will be 'overflowuid' if !sk_fullsock(sk_to_full_sk(skb->sk))
uint32_t sock_uid = bpf_get_socket_uid(skb);
@@ -470,7 +545,7 @@
// CLAT daemon receives via an untagged AF_PACKET socket.
if (egress.egress && uid == AID_CLAT) return PASS;
- int match = bpf_owner_match(skb, sock_uid, egress, kver);
+ int match = bpf_owner_match(skb, sock_uid, egress, kver, lvl);
// Workaround for secureVPN with VpnIsolation enabled, refer to b/159994981 for details.
// Keep TAG_SYSTEM_DNS in sync with DnsResolver/include/netd_resolv/resolv.h
@@ -483,6 +558,10 @@
if (match == DROP_UNLESS_DNS) match = DROP;
}
+ if (SDK_LEVEL_IS_AT_LEAST(lvl, 25Q2) && (match != DROP)) {
+ if (should_block_local_network_packets(skb, uid, egress, kver)) match = DROP;
+ }
+
// If an outbound packet is going to be dropped, we do not count that traffic.
if (egress.egress && (match == DROP)) return DROP;
@@ -496,7 +575,7 @@
if (!selectedMap) return PASS; // cannot happen, needed to keep bpf verifier happy
- do_packet_tracing(skb, egress, uid, tag, enable_tracing, kver);
+ do_packet_tracing(skb, egress, uid, tag, kver);
update_stats_with_config(*selectedMap, skb, &key, egress, kver);
update_app_uid_stats_map(skb, &uid, egress, kver);
@@ -509,52 +588,100 @@
return match;
}
-// Tracing on Android U+ 5.8+
-DEFINE_BPF_PROG_EXT("cgroupskb/ingress/stats$trace", AID_ROOT, AID_SYSTEM,
- bpf_cgroup_ingress_trace, KVER_5_8, KVER_INF,
- BPFLOADER_MAINLINE_U_VERSION, BPFLOADER_MAX_VER, MANDATORY,
- "fs_bpf_netd_readonly", "",
- LOAD_ON_ENG, LOAD_ON_USER, LOAD_ON_USERDEBUG)
+// -----
+
+// Supported kernel + platform/os version combinations:
+//
+// | 4.9 | 4.14 | 4.19 | 5.4 | 5.10 | 5.15 | 6.1 | 6.6 | 6.12 |
+// 25Q2 | | | | x | x | x | x | x | x |
+// V | | | x | x | x | x | x | x | |
+// U | | x | x | x | x | x | x | | |
+// T | x | x | x | x | x | x | | | |
+
+// ----- cgroupskb/ingress/stats -----
+
+// Android 25Q2+ 5.10+ (localnet protection + tracing)
+DEFINE_NETD_BPF_PROG_RANGES("cgroupskb/ingress/stats$5_10_25q2",
+ bpf_cgroup_ingress_5_10_25q2, KVER_5_10, KVER_INF,
+ BPFLOADER_MAINLINE_25Q2_VERSION, BPFLOADER_MAX_VER)
(struct __sk_buff* skb) {
- return bpf_traffic_account(skb, INGRESS, TRACE_ON, KVER_5_8);
+ return bpf_traffic_account(skb, INGRESS, KVER_5_10, SDK_LEVEL_25Q2);
}
-DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/ingress/stats$4_19", AID_ROOT, AID_SYSTEM,
+// Android 25Q2+ 5.4 (localnet protection)
+DEFINE_NETD_BPF_PROG_RANGES("cgroupskb/ingress/stats$5_4_25q2",
+ bpf_cgroup_ingress_5_4_25q2, KVER_5_4, KVER_5_10,
+ BPFLOADER_MAINLINE_25Q2_VERSION, BPFLOADER_MAX_VER)
+(struct __sk_buff* skb) {
+ return bpf_traffic_account(skb, INGRESS, KVER_5_4, SDK_LEVEL_25Q2);
+}
+
+// Android U/V 5.10+ (tracing)
+DEFINE_NETD_BPF_PROG_RANGES("cgroupskb/ingress/stats$5_10_u",
+ bpf_cgroup_ingress_5_10_u, KVER_5_10, KVER_INF,
+ BPFLOADER_MAINLINE_U_VERSION, BPFLOADER_MAINLINE_25Q2_VERSION)
+(struct __sk_buff* skb) {
+ return bpf_traffic_account(skb, INGRESS, KVER_5_10, SDK_LEVEL_U);
+}
+
+// Android T/U/V 4.19 & T/U/V/25Q2 5.4 & T 5.10/5.15
+DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/ingress/stats$4_19",
bpf_cgroup_ingress_4_19, KVER_4_19, KVER_INF)
(struct __sk_buff* skb) {
- return bpf_traffic_account(skb, INGRESS, TRACE_OFF, KVER_4_19);
+ return bpf_traffic_account(skb, INGRESS, KVER_4_19, SDK_LEVEL_T);
}
-DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/ingress/stats$4_14", AID_ROOT, AID_SYSTEM,
- bpf_cgroup_ingress_4_14, KVER_NONE, KVER_4_19)
+// Android T 4.9 & T/U 4.14
+DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/ingress/stats$4_9",
+ bpf_cgroup_ingress_4_9, KVER_NONE, KVER_4_19)
(struct __sk_buff* skb) {
- return bpf_traffic_account(skb, INGRESS, TRACE_OFF, KVER_NONE);
+ return bpf_traffic_account(skb, INGRESS, KVER_NONE, SDK_LEVEL_T);
}
-// Tracing on Android U+ 5.8+
-DEFINE_BPF_PROG_EXT("cgroupskb/egress/stats$trace", AID_ROOT, AID_SYSTEM,
- bpf_cgroup_egress_trace, KVER_5_8, KVER_INF,
- BPFLOADER_MAINLINE_U_VERSION, BPFLOADER_MAX_VER, MANDATORY,
- "fs_bpf_netd_readonly", "",
- LOAD_ON_ENG, LOAD_ON_USER, LOAD_ON_USERDEBUG)
+// ----- cgroupskb/egress/stats -----
+
+// Android 25Q2+ 5.10+ (localnet protection + tracing)
+DEFINE_NETD_BPF_PROG_RANGES("cgroupskb/egress/stats$5_10_25q2",
+ bpf_cgroup_egress_5_10_25q2, KVER_5_10, KVER_INF,
+ BPFLOADER_MAINLINE_25Q2_VERSION, BPFLOADER_MAX_VER)
(struct __sk_buff* skb) {
- return bpf_traffic_account(skb, EGRESS, TRACE_ON, KVER_5_8);
+ return bpf_traffic_account(skb, EGRESS, KVER_5_10, SDK_LEVEL_25Q2);
}
-DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/egress/stats$4_19", AID_ROOT, AID_SYSTEM,
+// Android 25Q2+ 5.4 (localnet protection)
+DEFINE_NETD_BPF_PROG_RANGES("cgroupskb/egress/stats$5_4_25q2",
+ bpf_cgroup_egress_5_4_25q2, KVER_5_4, KVER_5_10,
+ BPFLOADER_MAINLINE_25Q2_VERSION, BPFLOADER_MAX_VER)
+(struct __sk_buff* skb) {
+ return bpf_traffic_account(skb, EGRESS, KVER_5_4, SDK_LEVEL_25Q2);
+}
+
+// Android U/V 5.10+ (tracing)
+DEFINE_NETD_BPF_PROG_RANGES("cgroupskb/egress/stats$5_10_u",
+ bpf_cgroup_egress_5_10_u, KVER_5_10, KVER_INF,
+ BPFLOADER_MAINLINE_U_VERSION, BPFLOADER_MAINLINE_25Q2_VERSION)
+(struct __sk_buff* skb) {
+ return bpf_traffic_account(skb, EGRESS, KVER_5_10, SDK_LEVEL_U);
+}
+
+// Android T/U/V 4.19 & T/U/V/25Q2 5.4 & T 5.10/5.15
+DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/egress/stats$4_19",
bpf_cgroup_egress_4_19, KVER_4_19, KVER_INF)
(struct __sk_buff* skb) {
- return bpf_traffic_account(skb, EGRESS, TRACE_OFF, KVER_4_19);
+ return bpf_traffic_account(skb, EGRESS, KVER_4_19, SDK_LEVEL_T);
}
-DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/egress/stats$4_14", AID_ROOT, AID_SYSTEM,
- bpf_cgroup_egress_4_14, KVER_NONE, KVER_4_19)
+// Android T 4.9 & T/U 4.14
+DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/egress/stats$4_9",
+ bpf_cgroup_egress_4_9, KVER_NONE, KVER_4_19)
(struct __sk_buff* skb) {
- return bpf_traffic_account(skb, EGRESS, TRACE_OFF, KVER_NONE);
+ return bpf_traffic_account(skb, EGRESS, KVER_NONE, SDK_LEVEL_T);
}
+// -----
+
// WARNING: Android T's non-updatable netd depends on the name of this program.
-DEFINE_XTBPF_PROG("skfilter/egress/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_egress_prog)
+DEFINE_XTBPF_PROG("skfilter/egress/xtbpf", xt_bpf_egress_prog)
(struct __sk_buff* skb) {
// Clat daemon does not generate new traffic, all its traffic is accounted for already
// on the v4-* interfaces (except for the 20 (or 28) extra bytes of IPv6 vs IPv4 overhead,
@@ -573,7 +700,7 @@
}
// WARNING: Android T's non-updatable netd depends on the name of this program.
-DEFINE_XTBPF_PROG("skfilter/ingress/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_ingress_prog)
+DEFINE_XTBPF_PROG("skfilter/ingress/xtbpf", xt_bpf_ingress_prog)
(struct __sk_buff* skb) {
// Clat daemon traffic is not accounted by virtue of iptables raw prerouting drop rule
// (in clat_raw_PREROUTING chain), which triggers before this (in bw_raw_PREROUTING chain).
@@ -585,7 +712,7 @@
return XTBPF_MATCH;
}
-DEFINE_SYS_BPF_PROG("schedact/ingress/account", AID_ROOT, AID_NET_ADMIN,
+DEFINE_SYS_BPF_PROG("schedact/ingress/account",
tc_bpf_ingress_account_prog)
(struct __sk_buff* skb) {
if (is_received_skb(skb)) {
@@ -597,7 +724,7 @@
}
// WARNING: Android T's non-updatable netd depends on the name of this program.
-DEFINE_XTBPF_PROG("skfilter/allowlist/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_allowlist_prog)
+DEFINE_XTBPF_PROG("skfilter/allowlist/xtbpf", xt_bpf_allowlist_prog)
(struct __sk_buff* skb) {
uint32_t sock_uid = bpf_get_socket_uid(skb);
if (is_system_uid(sock_uid)) return XTBPF_MATCH;
@@ -616,7 +743,7 @@
}
// WARNING: Android T's non-updatable netd depends on the name of this program.
-DEFINE_XTBPF_PROG("skfilter/denylist/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_denylist_prog)
+DEFINE_XTBPF_PROG("skfilter/denylist/xtbpf", xt_bpf_denylist_prog)
(struct __sk_buff* skb) {
uint32_t sock_uid = bpf_get_socket_uid(skb);
UidOwnerValue* denylistMatch = bpf_uid_owner_map_lookup_elem(&sock_uid);
@@ -639,14 +766,12 @@
return permissions ? *permissions : BPF_PERMISSION_INTERNET;
}
-DEFINE_NETD_BPF_PROG_KVER("cgroupsock/inet_create", AID_ROOT, AID_ROOT, inet_socket_create,
- KVER_4_14)
+DEFINE_NETD_BPF_PROG_KVER("cgroupsock/inet_create", inet_socket_create, KVER_4_14)
(__unused struct bpf_sock* sk) {
return (get_app_permissions() & BPF_PERMISSION_INTERNET) ? BPF_ALLOW : BPF_DISALLOW;
}
-DEFINE_NETD_BPF_PROG_KVER("cgroupsockrelease/inet_release", AID_ROOT, AID_ROOT,
- inet_socket_release, KVER_5_10)
+DEFINE_NETD_BPF_PROG_KVER("cgroupsockrelease/inet_release", inet_socket_release, KVER_5_10)
(struct bpf_sock* sk) {
uint64_t cookie = bpf_get_sk_cookie(sk);
if (cookie) bpf_cookie_tag_map_delete_elem(&cookie);
@@ -699,47 +824,47 @@
return BPF_ALLOW;
}
-DEFINE_NETD_BPF_PROG_KVER("bind4/inet4_bind", AID_ROOT, AID_ROOT, inet4_bind, KVER_4_19)
+DEFINE_NETD_BPF_PROG_KVER("bind4/inet4_bind", inet4_bind, KVER_4_19)
(struct bpf_sock_addr *ctx) {
return block_port(ctx);
}
-DEFINE_NETD_BPF_PROG_KVER("bind6/inet6_bind", AID_ROOT, AID_ROOT, inet6_bind, KVER_4_19)
+DEFINE_NETD_BPF_PROG_KVER("bind6/inet6_bind", inet6_bind, KVER_4_19)
(struct bpf_sock_addr *ctx) {
return block_port(ctx);
}
-DEFINE_NETD_V_BPF_PROG_KVER("connect4/inet4_connect", AID_ROOT, AID_ROOT, inet4_connect, KVER_4_19)
+DEFINE_NETD_V_BPF_PROG_KVER("connect4/inet4_connect", inet4_connect, KVER_4_19)
(struct bpf_sock_addr *ctx) {
return check_localhost(ctx);
}
-DEFINE_NETD_V_BPF_PROG_KVER("connect6/inet6_connect", AID_ROOT, AID_ROOT, inet6_connect, KVER_4_19)
+DEFINE_NETD_V_BPF_PROG_KVER("connect6/inet6_connect", inet6_connect, KVER_4_19)
(struct bpf_sock_addr *ctx) {
return check_localhost(ctx);
}
-DEFINE_NETD_V_BPF_PROG_KVER("recvmsg4/udp4_recvmsg", AID_ROOT, AID_ROOT, udp4_recvmsg, KVER_4_19)
+DEFINE_NETD_V_BPF_PROG_KVER("recvmsg4/udp4_recvmsg", udp4_recvmsg, KVER_4_19)
(struct bpf_sock_addr *ctx) {
return check_localhost(ctx);
}
-DEFINE_NETD_V_BPF_PROG_KVER("recvmsg6/udp6_recvmsg", AID_ROOT, AID_ROOT, udp6_recvmsg, KVER_4_19)
+DEFINE_NETD_V_BPF_PROG_KVER("recvmsg6/udp6_recvmsg", udp6_recvmsg, KVER_4_19)
(struct bpf_sock_addr *ctx) {
return check_localhost(ctx);
}
-DEFINE_NETD_V_BPF_PROG_KVER("sendmsg4/udp4_sendmsg", AID_ROOT, AID_ROOT, udp4_sendmsg, KVER_4_19)
+DEFINE_NETD_V_BPF_PROG_KVER("sendmsg4/udp4_sendmsg", udp4_sendmsg, KVER_4_19)
(struct bpf_sock_addr *ctx) {
return check_localhost(ctx);
}
-DEFINE_NETD_V_BPF_PROG_KVER("sendmsg6/udp6_sendmsg", AID_ROOT, AID_ROOT, udp6_sendmsg, KVER_4_19)
+DEFINE_NETD_V_BPF_PROG_KVER("sendmsg6/udp6_sendmsg", udp6_sendmsg, KVER_4_19)
(struct bpf_sock_addr *ctx) {
return check_localhost(ctx);
}
-DEFINE_NETD_V_BPF_PROG_KVER("getsockopt/prog", AID_ROOT, AID_ROOT, getsockopt_prog, KVER_5_4)
+DEFINE_NETD_V_BPF_PROG_KVER("getsockopt/prog", getsockopt_prog, KVER_5_4)
(struct bpf_sockopt *ctx) {
// Tell kernel to return 'original' kernel reply (instead of the bpf modified buffer)
// This is important if the answer is larger than PAGE_SIZE (max size this bpf hook can provide)
@@ -747,7 +872,7 @@
return BPF_ALLOW;
}
-DEFINE_NETD_V_BPF_PROG_KVER("setsockopt/prog", AID_ROOT, AID_ROOT, setsockopt_prog, KVER_5_4)
+DEFINE_NETD_V_BPF_PROG_KVER("setsockopt/prog", setsockopt_prog, KVER_5_4)
(struct bpf_sockopt *ctx) {
// Tell kernel to use/process original buffer provided by userspace.
// This is important if it is larger than PAGE_SIZE (max size this bpf hook can handle).
diff --git a/bpf/progs/netd.h b/bpf/progs/netd.h
index be7c311..8400679 100644
--- a/bpf/progs/netd.h
+++ b/bpf/progs/netd.h
@@ -185,6 +185,8 @@
#define PACKET_TRACE_RINGBUF_PATH BPF_NETD_PATH "map_netd_packet_trace_ringbuf"
#define PACKET_TRACE_ENABLED_MAP_PATH BPF_NETD_PATH "map_netd_packet_trace_enabled_map"
#define DATA_SAVER_ENABLED_MAP_PATH BPF_NETD_PATH "map_netd_data_saver_enabled_map"
+#define LOCAL_NET_ACCESS_MAP_PATH BPF_NETD_PATH "map_netd_local_net_access_map"
+#define LOCAL_NET_BLOCKED_UID_MAP_PATH BPF_NETD_PATH "map_netd_local_net_blocked_uid_map"
#endif // __cplusplus
@@ -245,6 +247,18 @@
} IngressDiscardValue;
STRUCT_SIZE(IngressDiscardValue, 2 * 4); // 8
+typedef struct {
+ // Longest prefix match length in bits (value from 0 to 192).
+ uint32_t lpm_bitlen;
+ uint32_t if_index;
+ // IPv4 uses IPv4-mapped IPv6 address format.
+ struct in6_addr remote_ip6;
+ // u16 instead of u8 to avoid padding due to alignment requirement.
+ uint16_t protocol;
+ __be16 remote_port;
+} LocalNetAccessKey;
+STRUCT_SIZE(LocalNetAccessKey, 4 + 4 + 16 + 2 + 2); // 28
+
// Entry in the configuration map that stores which UID rules are enabled.
#define UID_RULES_CONFIGURATION_KEY 0
// Entry in the configuration map that stores which stats map is currently in use.
diff --git a/bpf/progs/offload.c b/bpf/progs/offload.c
index 631908a..0f23844 100644
--- a/bpf/progs/offload.c
+++ b/bpf/progs/offload.c
@@ -609,27 +609,27 @@
// Full featured (required) implementations for 5.8+ kernels (these are S+ by definition)
DEFINE_BPF_PROG_KVER("schedcls/tether_downstream4_rawip$5_8", AID_ROOT, AID_NETWORK_STACK,
- sched_cls_tether_downstream4_rawip_5_8, KVER_5_8)
+ sched_cls_tether_downstream4_rawip_5_8, KVER_5_10)
(struct __sk_buff* skb) {
- return do_forward4(skb, RAWIP, DOWNSTREAM, UPDATETIME, KVER_5_8);
+ return do_forward4(skb, RAWIP, DOWNSTREAM, UPDATETIME, KVER_5_10);
}
DEFINE_BPF_PROG_KVER("schedcls/tether_upstream4_rawip$5_8", AID_ROOT, AID_NETWORK_STACK,
- sched_cls_tether_upstream4_rawip_5_8, KVER_5_8)
+ sched_cls_tether_upstream4_rawip_5_8, KVER_5_10)
(struct __sk_buff* skb) {
- return do_forward4(skb, RAWIP, UPSTREAM, UPDATETIME, KVER_5_8);
+ return do_forward4(skb, RAWIP, UPSTREAM, UPDATETIME, KVER_5_10);
}
DEFINE_BPF_PROG_KVER("schedcls/tether_downstream4_ether$5_8", AID_ROOT, AID_NETWORK_STACK,
- sched_cls_tether_downstream4_ether_5_8, KVER_5_8)
+ sched_cls_tether_downstream4_ether_5_8, KVER_5_10)
(struct __sk_buff* skb) {
- return do_forward4(skb, ETHER, DOWNSTREAM, UPDATETIME, KVER_5_8);
+ return do_forward4(skb, ETHER, DOWNSTREAM, UPDATETIME, KVER_5_10);
}
DEFINE_BPF_PROG_KVER("schedcls/tether_upstream4_ether$5_8", AID_ROOT, AID_NETWORK_STACK,
- sched_cls_tether_upstream4_ether_5_8, KVER_5_8)
+ sched_cls_tether_upstream4_ether_5_8, KVER_5_10)
(struct __sk_buff* skb) {
- return do_forward4(skb, ETHER, UPSTREAM, UPDATETIME, KVER_5_8);
+ return do_forward4(skb, ETHER, UPSTREAM, UPDATETIME, KVER_5_10);
}
// Full featured (optional) implementations for 4.14-S, 4.19-S & 5.4-S kernels
@@ -638,7 +638,7 @@
DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_rawip$opt",
AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_downstream4_rawip_opt,
- KVER_4_14, KVER_5_8)
+ KVER_4_14, KVER_5_10)
(struct __sk_buff* skb) {
return do_forward4(skb, RAWIP, DOWNSTREAM, UPDATETIME, KVER_4_14);
}
@@ -646,7 +646,7 @@
DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_rawip$opt",
AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_upstream4_rawip_opt,
- KVER_4_14, KVER_5_8)
+ KVER_4_14, KVER_5_10)
(struct __sk_buff* skb) {
return do_forward4(skb, RAWIP, UPSTREAM, UPDATETIME, KVER_4_14);
}
@@ -654,7 +654,7 @@
DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_ether$opt",
AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_downstream4_ether_opt,
- KVER_4_14, KVER_5_8)
+ KVER_4_14, KVER_5_10)
(struct __sk_buff* skb) {
return do_forward4(skb, ETHER, DOWNSTREAM, UPDATETIME, KVER_4_14);
}
@@ -662,7 +662,7 @@
DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_ether$opt",
AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_upstream4_ether_opt,
- KVER_4_14, KVER_5_8)
+ KVER_4_14, KVER_5_10)
(struct __sk_buff* skb) {
return do_forward4(skb, ETHER, UPSTREAM, UPDATETIME, KVER_4_14);
}
@@ -682,13 +682,13 @@
// RAWIP: Required for 5.4-R kernels -- which always support bpf_skb_change_head().
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_rawip$5_4", AID_ROOT, AID_NETWORK_STACK,
- sched_cls_tether_downstream4_rawip_5_4, KVER_5_4, KVER_5_8)
+ sched_cls_tether_downstream4_rawip_5_4, KVER_5_4, KVER_5_10)
(struct __sk_buff* skb) {
return do_forward4(skb, RAWIP, DOWNSTREAM, NO_UPDATETIME, KVER_5_4);
}
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_rawip$5_4", AID_ROOT, AID_NETWORK_STACK,
- sched_cls_tether_upstream4_rawip_5_4, KVER_5_4, KVER_5_8)
+ sched_cls_tether_upstream4_rawip_5_4, KVER_5_4, KVER_5_10)
(struct __sk_buff* skb) {
return do_forward4(skb, RAWIP, UPSTREAM, NO_UPDATETIME, KVER_5_4);
}
@@ -715,13 +715,13 @@
// ETHER: Required for 4.14-Q/R, 4.19-Q/R & 5.4-R kernels.
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_ether$4_14", AID_ROOT, AID_NETWORK_STACK,
- sched_cls_tether_downstream4_ether_4_14, KVER_4_14, KVER_5_8)
+ sched_cls_tether_downstream4_ether_4_14, KVER_4_14, KVER_5_10)
(struct __sk_buff* skb) {
return do_forward4(skb, ETHER, DOWNSTREAM, NO_UPDATETIME, KVER_4_14);
}
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_ether$4_14", AID_ROOT, AID_NETWORK_STACK,
- sched_cls_tether_upstream4_ether_4_14, KVER_4_14, KVER_5_8)
+ sched_cls_tether_upstream4_ether_4_14, KVER_4_14, KVER_5_10)
(struct __sk_buff* skb) {
return do_forward4(skb, ETHER, UPSTREAM, NO_UPDATETIME, KVER_4_14);
}
@@ -805,7 +805,7 @@
}
#define DEFINE_XDP_PROG(str, func) \
- DEFINE_BPF_PROG_KVER(str, AID_ROOT, AID_NETWORK_STACK, func, KVER_5_9)(struct xdp_md *ctx)
+ DEFINE_BPF_PROG_KVER(str, AID_ROOT, AID_NETWORK_STACK, func, KVER_5_10)(struct xdp_md *ctx)
DEFINE_XDP_PROG("xdp/tether_downstream_ether",
xdp_tether_downstream_ether) {
diff --git a/bpf/tests/mts/bpf_existence_test.cpp b/bpf/tests/mts/bpf_existence_test.cpp
index 0b5b7be..2cfa546 100644
--- a/bpf/tests/mts/bpf_existence_test.cpp
+++ b/bpf/tests/mts/bpf_existence_test.cpp
@@ -20,7 +20,9 @@
#include <set>
#include <string>
+#include <android-base/properties.h>
#include <android-modules-utils/sdk_level.h>
+#include <android/api-level.h>
#include <bpf/BpfUtils.h>
#include <gtest/gtest.h>
@@ -46,6 +48,11 @@
class BpfExistenceTest : public ::testing::Test {
};
+//ToDo: replace isAtLeast25Q2 with IsAtLeastB once sdk_level have been upgraded to 36 on aosp/main
+const bool unreleased = (android::base::GetProperty("ro.build.version.codename", "REL") != "REL");
+const int api_level = unreleased ? __ANDROID_API_FUTURE__ : android_get_device_api_level();
+const bool isAtLeast25Q2 = (api_level > __ANDROID_API_V__);
+
// Part of Android R platform (for 4.9+), but mainlined in S
static const set<string> PLATFORM_ONLY_IN_R = {
PLATFORM "map_offload_tether_ingress_map",
@@ -159,6 +166,12 @@
NETD "prog_netd_setsockopt_prog",
};
+// Provided by *current* mainline module for 25Q2+ devices
+static const set<string> MAINLINE_FOR_25Q2_PLUS = {
+ NETD "map_netd_local_net_access_map",
+ NETD "map_netd_local_net_blocked_uid_map",
+};
+
static void addAll(set<string>& a, const set<string>& b) {
a.insert(b.begin(), b.end());
}
@@ -209,6 +222,9 @@
DO_EXPECT(IsAtLeastV(), MAINLINE_FOR_V_PLUS);
DO_EXPECT(IsAtLeastV() && isAtLeastKernelVersion(5, 4, 0), MAINLINE_FOR_V_5_4_PLUS);
+ if (isAtLeast25Q2) ASSERT_TRUE(isAtLeastKernelVersion(5, 4, 0));
+ DO_EXPECT(isAtLeast25Q2, MAINLINE_FOR_25Q2_PLUS);
+
for (const auto& file : mustExist) {
EXPECT_EQ(0, access(file.c_str(), R_OK)) << file << " does not exist";
}
diff --git a/networksecurity/service/src/com/android/server/net/ct/SignatureVerifier.java b/networksecurity/service/src/com/android/server/net/ct/SignatureVerifier.java
index 96488fc..67ef63f 100644
--- a/networksecurity/service/src/com/android/server/net/ct/SignatureVerifier.java
+++ b/networksecurity/service/src/com/android/server/net/ct/SignatureVerifier.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.net.Uri;
import android.os.Build;
+import android.util.Log;
import androidx.annotation.VisibleForTesting;
@@ -62,10 +63,15 @@
}
void setPublicKey(String publicKey) throws GeneralSecurityException {
+ byte[] decodedPublicKey = null;
+ try {
+ decodedPublicKey = Base64.getDecoder().decode(publicKey);
+ } catch (IllegalArgumentException e) {
+ throw new GeneralSecurityException("Invalid public key base64 encoding", e);
+ }
setPublicKey(
KeyFactory.getInstance("RSA")
- .generatePublic(
- new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey))));
+ .generatePublic(new X509EncodedKeySpec(decodedPublicKey)));
}
@VisibleForTesting
@@ -82,10 +88,21 @@
verifier.initVerify(mPublicKey.get());
ContentResolver contentResolver = mContext.getContentResolver();
+ boolean success = false;
try (InputStream fileStream = contentResolver.openInputStream(file);
InputStream signatureStream = contentResolver.openInputStream(signature)) {
verifier.update(fileStream.readAllBytes());
- return verifier.verify(signatureStream.readAllBytes());
+
+ byte[] signatureBytes = signatureStream.readAllBytes();
+ try {
+ success = verifier.verify(Base64.getDecoder().decode(signatureBytes));
+ } catch (IllegalArgumentException e) {
+ Log.w("CertificateTransparencyDownloader", "Invalid signature base64 encoding", e);
+ // TODO: remove the fallback once the signature base64 is published
+ Log.i("CertificateTransparencyDownloader", "Signature verification as raw bytes");
+ success = verifier.verify(signatureBytes);
+ }
}
+ return success;
}
}
diff --git a/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java b/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java
index 2f57fc9..78e7272 100644
--- a/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java
+++ b/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java
@@ -235,8 +235,8 @@
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
.isEqualTo(1);
verify(mLogger, never()).logCTLogListUpdateFailedEvent(anyInt(), anyInt());
- verify(mLogger, never()).logCTLogListUpdateFailedEventWithDownloadStatus(
- anyInt(), anyInt());
+ verify(mLogger, never())
+ .logCTLogListUpdateFailedEventWithDownloadStatus(anyInt(), anyInt());
}
@Test
@@ -309,8 +309,8 @@
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
.isEqualTo(1);
verify(mLogger, never()).logCTLogListUpdateFailedEvent(anyInt(), anyInt());
- verify(mLogger, never()).logCTLogListUpdateFailedEventWithDownloadStatus(
- anyInt(), anyInt());
+ verify(mLogger, never())
+ .logCTLogListUpdateFailedEventWithDownloadStatus(anyInt(), anyInt());
}
@Test
@@ -387,8 +387,8 @@
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
.isEqualTo(1);
verify(mLogger, never()).logCTLogListUpdateFailedEvent(anyInt(), anyInt());
- verify(mLogger, never()).logCTLogListUpdateFailedEventWithDownloadStatus(
- anyInt(), anyInt());
+ verify(mLogger, never())
+ .logCTLogListUpdateFailedEventWithDownloadStatus(anyInt(), anyInt());
}
@Test
@@ -606,8 +606,8 @@
Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0))
.isEqualTo(1);
verify(mLogger, never()).logCTLogListUpdateFailedEvent(anyInt(), anyInt());
- verify(mLogger, never()).logCTLogListUpdateFailedEventWithDownloadStatus(
- anyInt(), anyInt());
+ verify(mLogger, never())
+ .logCTLogListUpdateFailedEventWithDownloadStatus(anyInt(), anyInt());
}
@Test
@@ -793,7 +793,7 @@
try (InputStream fileStream = new FileInputStream(file);
OutputStream outputStream = new FileOutputStream(signatureFile)) {
signer.update(fileStream.readAllBytes());
- outputStream.write(signer.sign());
+ outputStream.write(Base64.getEncoder().encode(signer.sign()));
}
return signatureFile;
diff --git a/remoteauth/OWNERS b/remoteauth/OWNERS
index 25a32b9..ee46c1c 100644
--- a/remoteauth/OWNERS
+++ b/remoteauth/OWNERS
@@ -2,7 +2,6 @@
# Bug template url: http://b/new?component=1145231&template=1715387
billyhuang@google.com
boetger@google.com
-casbor@google.com
derekjedral@google.com
dlm@google.com
igorzas@google.com
diff --git a/staticlibs/device/com/android/net/module/util/RealtimeScheduler.java b/staticlibs/device/com/android/net/module/util/RealtimeScheduler.java
index a556ccc..c8fdf72 100644
--- a/staticlibs/device/com/android/net/module/util/RealtimeScheduler.java
+++ b/staticlibs/device/com/android/net/module/util/RealtimeScheduler.java
@@ -255,6 +255,10 @@
if (!isRunning()) {
return 0;
}
+ if ((events & EVENT_ERROR) != 0) {
+ Log.wtf(TAG, "Got EVENT_ERROR from FileDescriptorEventListener.");
+ return 0;
+ }
if ((events & EVENT_INPUT) != 0) {
handleExpiration();
}
diff --git a/staticlibs/device/com/android/net/module/util/netlink/NetlinkUtils.java b/staticlibs/device/com/android/net/module/util/netlink/NetlinkUtils.java
index e2544d3..0d96fc4 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/NetlinkUtils.java
+++ b/staticlibs/device/com/android/net/module/util/netlink/NetlinkUtils.java
@@ -83,7 +83,7 @@
public static final int INET_DIAG_INFO = 2;
public static final int INET_DIAG_MARK = 15;
- public static final long IO_TIMEOUT_MS = 300L;
+ public static final long IO_TIMEOUT_MS = 3000L;
public static final int DEFAULT_RECV_BUFSIZE = 8 * 1024;
public static final int SOCKET_RECV_BUFSIZE = 64 * 1024;
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt b/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt
new file mode 100644
index 0000000..a93ae3e
--- /dev/null
+++ b/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.testutils.com.android.testutils
+
+import android.Manifest.permission.MODIFY_PHONE_STATE
+import android.Manifest.permission.READ_PHONE_STATE
+import android.os.PersistableBundle
+import android.telephony.CarrierConfigManager
+import android.util.Log
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.modules.utils.build.SdkLevel.isAtLeastU
+import com.android.testutils.runAsShell
+import com.android.testutils.tryTest
+import kotlin.test.assertNotNull
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+private val TAG = CarrierConfigRule::class.simpleName
+
+/**
+ * A [TestRule] that helps set [CarrierConfigManager] overrides for tests and clean up the test
+ * configuration automatically on teardown.
+ */
+class CarrierConfigRule : TestRule {
+ private val ccm by lazy { InstrumentationRegistry.getInstrumentation().context.getSystemService(
+ CarrierConfigManager::class.java
+ ) }
+
+ // Map of (subId) -> (original values of overridden settings)
+ private val originalConfigs = mutableMapOf<Int, PersistableBundle>()
+
+ override fun apply(base: Statement, description: Description): Statement {
+ return CarrierConfigStatement(base, description)
+ }
+
+ private inner class CarrierConfigStatement(
+ private val base: Statement,
+ private val description: Description
+ ) : Statement() {
+ override fun evaluate() {
+ tryTest {
+ base.evaluate()
+ } cleanup {
+ cleanUpNow()
+ }
+ }
+ }
+
+ /**
+ * Add carrier config overrides with the specified configuration.
+ *
+ * The overrides will automatically be cleaned up when the test case finishes.
+ */
+ fun addConfigOverrides(subId: Int, config: PersistableBundle) {
+ val originalConfig = originalConfigs.computeIfAbsent(subId) { PersistableBundle() }
+ val overrideKeys = config.keySet()
+ val previousValues = runAsShell(READ_PHONE_STATE) {
+ ccm.getConfigForSubIdCompat(subId, overrideKeys)
+ }
+ // If a key is already in the originalConfig, keep the oldest original overrides
+ originalConfig.keySet().forEach {
+ previousValues.remove(it)
+ }
+ originalConfig.putAll(previousValues)
+
+ runAsShell(MODIFY_PHONE_STATE) {
+ ccm.overrideConfig(subId, config)
+ }
+ }
+
+ /**
+ * Cleanup overrides that were added by the test case.
+ *
+ * This will be called automatically on test teardown, so it does not need to be called by the
+ * test case unless cleaning up earlier is required.
+ */
+ fun cleanUpNow() {
+ runAsShell(MODIFY_PHONE_STATE) {
+ originalConfigs.forEach { (subId, config) ->
+ try {
+ // Do not use overrideConfig with null, as it would reset configs that may
+ // have been set by target preparers such as
+ // ConnectivityTestTargetPreparer / CarrierConfigSetupTest.
+ ccm.overrideConfig(subId, config)
+ } catch (e: Throwable) {
+ Log.e(TAG, "Error resetting carrier config for subId $subId")
+ }
+ }
+ originalConfigs.clear()
+ }
+ }
+}
+
+private fun CarrierConfigManager.getConfigForSubIdCompat(
+ subId: Int,
+ keys: Set<String>
+): PersistableBundle {
+ return if (isAtLeastU()) {
+ // This method is U+
+ getConfigForSubId(subId, *keys.toTypedArray())
+ } else {
+ @Suppress("DEPRECATION")
+ val config = assertNotNull(getConfigForSubId(subId))
+ val allKeys = config.keySet().toList()
+ allKeys.forEach {
+ if (!keys.contains(it)) {
+ config.remove(it)
+ }
+ }
+ config
+ }
+}
diff --git a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
index 3ab6c0d..9379697 100644
--- a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
+++ b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
@@ -53,6 +53,7 @@
import android.os.HandlerThread
import android.os.PowerManager
import android.os.UserManager
+import android.os.SystemProperties
import android.platform.test.annotations.AppModeFull
import android.provider.DeviceConfig
import android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY
@@ -423,6 +424,10 @@
assume().that(caps.apfVersionSupported).isAtLeast(version)
}
+ fun assumeNotCuttlefish() {
+ assume().that(SystemProperties.get("ro.product.board", "")).isNotEqualTo("cutf")
+ }
+
fun installProgram(bytes: ByteArray) {
val prog = bytes.toHexString()
val result = runShellCommandOrThrow("cmd network_stack apf $ifname install $prog").trim()
@@ -506,6 +511,7 @@
// should be turned on.
assume().that(getVsrApiLevel()).isAtLeast(34)
assumeApfVersionSupportAtLeast(4)
+ assumeNotCuttlefish()
// clear any active APF filter
clearApfMemory()
@@ -558,6 +564,7 @@
assume().that(getVsrApiLevel()).isAtLeast(34)
// Test v4 memory slots on both v4 and v6 interpreters.
assumeApfVersionSupportAtLeast(4)
+ assumeNotCuttlefish()
clearApfMemory()
val gen = ApfV4Generator(
caps.apfVersionSupported,
@@ -616,6 +623,7 @@
// should be turned on.
assume().that(getVsrApiLevel()).isAtLeast(34)
assumeApfVersionSupportAtLeast(4)
+ assumeNotCuttlefish()
clearApfMemory()
val gen = ApfV4Generator(
caps.apfVersionSupported,
@@ -658,6 +666,7 @@
@Test
fun testFilterAge16384thsIncreasesBetweenPackets() {
assumeApfVersionSupportAtLeast(6000)
+ assumeNotCuttlefish()
clearApfMemory()
val gen = ApfV6Generator(
caps.apfVersionSupported,
@@ -707,6 +716,7 @@
@Test
fun testReplyPing() {
assumeApfVersionSupportAtLeast(6000)
+ assumeNotCuttlefish()
installProgram(ByteArray(caps.maximumApfProgramSize) { 0 }) // Clear previous program
readProgram() // Ensure installation is complete
@@ -799,8 +809,12 @@
Log.i(TAG, "counter map: ${apfCounterTracker.counters}")
assertThat(replyPayloads.size).isEqualTo(expectReplyPayloads.size)
- for (i in replyPayloads.indices) {
- assertThat(replyPayloads[i]).isEqualTo(expectReplyPayloads[i])
+
+ // Sort the payload list before comparison to ensure consistency.
+ val sortedReplyPayloads = replyPayloads.sortedBy { it[0] }
+ val sortedExpectReplyPayloads = expectReplyPayloads.sortedBy { it[0] }
+ for (i in sortedReplyPayloads.indices) {
+ assertThat(sortedReplyPayloads[i]).isEqualTo(sortedExpectReplyPayloads[i])
}
}
}
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
index ceb48d4..faaadee 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
@@ -88,9 +88,11 @@
import com.android.net.module.util.ArrayTrackRecord;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
+import com.android.testutils.com.android.testutils.CarrierConfigRule;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -110,6 +112,9 @@
public class ConnectivityDiagnosticsManagerTest {
private static final String TAG = ConnectivityDiagnosticsManagerTest.class.getSimpleName();
+ @Rule
+ public final CarrierConfigRule mCarrierConfigRule = new CarrierConfigRule();
+
private static final int CALLBACK_TIMEOUT_MILLIS = 5000;
private static final int NO_CALLBACK_INVOKED_TIMEOUT = 500;
private static final long TIMESTAMP = 123456789L;
@@ -264,9 +269,6 @@
doBroadcastCarrierConfigsAndVerifyOnConnectivityReportAvailable(
subId, carrierConfigReceiver, testNetworkCallback);
}, () -> {
- runWithShellPermissionIdentity(
- () -> mCarrierConfigManager.overrideConfig(subId, null),
- android.Manifest.permission.MODIFY_PHONE_STATE);
mConnectivityManager.unregisterNetworkCallback(testNetworkCallback);
mContext.unregisterReceiver(carrierConfigReceiver);
});
@@ -291,9 +293,9 @@
CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY,
new String[] {getCertHashForThisPackage()});
+ mCarrierConfigRule.addConfigOverrides(subId, carrierConfigs);
runWithShellPermissionIdentity(
() -> {
- mCarrierConfigManager.overrideConfig(subId, carrierConfigs);
mCarrierConfigManager.notifyConfigChangedForSubId(subId);
},
android.Manifest.permission.MODIFY_PHONE_STATE);
diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
index 5b2c9f7..57bc2be 100644
--- a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
@@ -1069,6 +1069,9 @@
@Test
fun testSetTetheringInterfaceMode_disableEnableEthernet() {
+ // do not run this test if an interface that can be used for tethering already exists.
+ assumeNoInterfaceForTetheringAvailable()
+
val listener = EthernetStateListener()
addInterfaceStateListener(listener)
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 815c3a5..286e08c 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -117,13 +117,13 @@
import com.android.testutils.ConnectivityModuleTest
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.PollPacketReader
import com.android.testutils.RecorderCallback.CallbackEntry.Available
import com.android.testutils.RecorderCallback.CallbackEntry.BlockedStatus
import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
import com.android.testutils.RecorderCallback.CallbackEntry.Losing
import com.android.testutils.RecorderCallback.CallbackEntry.Lost
-import com.android.testutils.PollPacketReader
import com.android.testutils.TestableNetworkAgent
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnAddKeepalivePacketFilter
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnAutomaticReconnectDisabled
@@ -140,6 +140,7 @@
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnValidationStatus
import com.android.testutils.TestableNetworkCallback
import com.android.testutils.assertThrows
+import com.android.testutils.com.android.testutils.CarrierConfigRule
import com.android.testutils.runAsShell
import com.android.testutils.tryTest
import com.android.testutils.waitForIdle
@@ -149,8 +150,8 @@
import java.net.InetAddress
import java.net.InetSocketAddress
import java.net.Socket
-import java.security.MessageDigest
import java.nio.ByteBuffer
+import java.security.MessageDigest
import java.time.Duration
import java.util.Arrays
import java.util.Random
@@ -167,6 +168,7 @@
import org.junit.After
import org.junit.Assume.assumeTrue
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
@@ -178,10 +180,12 @@
import org.mockito.Mockito.verify
private const val TAG = "NetworkAgentTest"
+
// This test doesn't really have a constraint on how fast the methods should return. If it's
// going to fail, it will simply wait forever, so setting a high timeout lowers the flake ratio
// without affecting the run time of successful runs. Thus, set a very high timeout.
private const val DEFAULT_TIMEOUT_MS = 5000L
+
// When waiting for a NetworkCallback to determine there was no timeout, waiting is the
// only possible thing (the relevant handler is the one in the real ConnectivityService,
// and then there is the Binder call), so have a short timeout for this as it will be
@@ -223,6 +227,9 @@
@IgnoreUpTo(Build.VERSION_CODES.R)
@RunWith(DevSdkIgnoreRunner::class)
class NetworkAgentTest {
+ @get:Rule
+ val carrierConfigRule = CarrierConfigRule()
+
private val LOCAL_IPV4_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.1")
private val REMOTE_IPV4_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.2")
@@ -378,8 +385,12 @@
// Ensure this NetworkAgent is never unneeded by filing a request with its specifier.
requestNetwork(makeTestNetworkRequest(specifier), callback)
val nc = makeTestNetworkCapabilities(specifier, transports)
- val agent = createNetworkAgent(context, initialConfig = initialConfig, initialLp = lp,
- initialNc = nc)
+ val agent = createNetworkAgent(
+ context,
+ initialConfig = initialConfig,
+ initialLp = lp,
+ initialNc = nc
+ )
agent.setTeardownDelayMillis(0)
// Connect the agent and verify initial status callbacks.
agent.register()
@@ -414,7 +425,8 @@
private fun createTunInterface(addrs: Collection<LinkAddress> = emptyList()):
TestNetworkInterface = realContext.getSystemService(
- TestNetworkManager::class.java)!!.createTunInterface(addrs).also {
+ TestNetworkManager::class.java
+ )!!.createTunInterface(addrs).also {
ifacesToCleanUp.add(it)
}
@@ -546,9 +558,12 @@
@Test
fun testSocketKeepalive(): Unit = createNetworkAgentWithFakeCS().let { agent ->
val packet = NattKeepalivePacketData(
- LOCAL_IPV4_ADDRESS /* srcAddress */, 1234 /* srcPort */,
- REMOTE_IPV4_ADDRESS /* dstAddress */, 4567 /* dstPort */,
- ByteArray(100 /* size */))
+ LOCAL_IPV4_ADDRESS /* srcAddress */,
+ 1234 /* srcPort */,
+ REMOTE_IPV4_ADDRESS /* dstAddress */,
+ 4567 /* dstPort */,
+ ByteArray(100 /* size */)
+ )
val slot = 4
val interval = 37
@@ -653,8 +668,13 @@
uid: Int,
expectUidsPresent: Boolean
) {
- doTestAllowedUids(intArrayOf(transport), uid, expectUidsPresent,
- specifier = null, transportInfo = null)
+ doTestAllowedUids(
+ intArrayOf(transport),
+ uid,
+ expectUidsPresent,
+ specifier = null,
+ transportInfo = null
+ )
}
private fun doTestAllowedUidsWithSubId(
@@ -689,15 +709,16 @@
private fun setHoldCarrierPrivilege(hold: Boolean, subId: Int) {
fun getCertHash(): String {
- val pkgInfo = realContext.packageManager.getPackageInfo(realContext.opPackageName,
- PackageManager.GET_SIGNATURES)
+ val pkgInfo = realContext.packageManager.getPackageInfo(
+ realContext.opPackageName,
+ PackageManager.GET_SIGNATURES
+ )
val digest = MessageDigest.getInstance("SHA-256")
val certHash = digest.digest(pkgInfo.signatures!![0]!!.toByteArray())
return UiccUtil.bytesToHexString(certHash)!!
}
val tm = realContext.getSystemService(TelephonyManager::class.java)!!
- val ccm = realContext.getSystemService(CarrierConfigManager::class.java)!!
val cv = ConditionVariable()
val cpb = PrivilegeWaiterCallback(cv)
@@ -717,16 +738,13 @@
return@tryTest
}
cv.close()
- runAsShell(MODIFY_PHONE_STATE) {
- val carrierConfigs = if (hold) {
- PersistableBundle().also {
- it.putStringArray(CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY,
- arrayOf(getCertHash()))
- }
- } else {
- null
- }
- ccm.overrideConfig(subId, carrierConfigs)
+ if (hold) {
+ carrierConfigRule.addConfigOverrides(subId, PersistableBundle().also {
+ it.putStringArray(CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY,
+ arrayOf(getCertHash()))
+ })
+ } else {
+ carrierConfigRule.cleanUpNow()
}
assertTrue(cv.block(DEFAULT_TIMEOUT_MS), "Can't change carrier privilege")
} cleanup {
@@ -799,14 +817,19 @@
val uid = try {
realContext.packageManager.getApplicationInfo(servicePackage, 0).uid
} catch (e: PackageManager.NameNotFoundException) {
- fail("$servicePackage could not be installed, please check the SuiteApkInstaller" +
- " installed CtsCarrierServicePackage.apk", e)
+ fail(
+ "$servicePackage could not be installed, please check the SuiteApkInstaller" +
+ " installed CtsCarrierServicePackage.apk",
+ e
+ )
}
val tm = realContext.getSystemService(TelephonyManager::class.java)!!
val defaultSubId = SubscriptionManager.getDefaultSubscriptionId()
- assertTrue(defaultSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID,
- "getDefaultSubscriptionId returns INVALID_SUBSCRIPTION_ID")
+ assertTrue(
+ defaultSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ "getDefaultSubscriptionId returns INVALID_SUBSCRIPTION_ID"
+ )
tryTest {
// This process is not the carrier service UID, so allowedUids should be ignored in all
// the following cases.
@@ -910,8 +933,10 @@
// If using the int ranking, agent1 must be upgraded to a better score so that there is
// no ambiguity when agent2 connects that agent1 is still better. If using policy
// ranking, this is not necessary.
- agent1.sendNetworkScore(NetworkScore.Builder().setLegacyInt(BETTER_NETWORK_SCORE)
- .build())
+ agent1.sendNetworkScore(
+ NetworkScore.Builder().setLegacyInt(BETTER_NETWORK_SCORE)
+ .build()
+ )
// Connect the second agent.
val (agent2, _) = createConnectedNetworkAgent()
@@ -920,10 +945,12 @@
// virtue of already satisfying the request.
callback.assertNoCallback(NO_CALLBACK_TIMEOUT)
// Now downgrade the score and expect the callback now prefers agent2
- agent1.sendNetworkScore(NetworkScore.Builder()
+ agent1.sendNetworkScore(
+ NetworkScore.Builder()
.setLegacyInt(WORSE_NETWORK_SCORE)
.setExiting(true)
- .build())
+ .build()
+ )
callback.expect<Available>(agent2.network!!)
// tearDown() will unregister the requests and agents
@@ -968,16 +995,20 @@
// Check that the default network's transport is propagated to the VPN.
var vpnNc = mCM.getNetworkCapabilities(agent.network!!)
assertNotNull(vpnNc)
- assertEquals(VpnManager.TYPE_VPN_SERVICE,
- (vpnNc.transportInfo as VpnTransportInfo).type)
+ assertEquals(
+ VpnManager.TYPE_VPN_SERVICE,
+ (vpnNc.transportInfo as VpnTransportInfo).type
+ )
assertEquals(mySessionId, (vpnNc.transportInfo as VpnTransportInfo).sessionId)
val testAndVpn = intArrayOf(TRANSPORT_TEST, TRANSPORT_VPN)
assertTrue(vpnNc.hasAllTransports(testAndVpn))
assertFalse(vpnNc.hasCapability(NET_CAPABILITY_NOT_VPN))
- assertTrue(vpnNc.hasAllTransports(defaultNetworkTransports),
- "VPN transports ${Arrays.toString(vpnNc.transportTypes)}" +
- " lacking transports from ${Arrays.toString(defaultNetworkTransports)}")
+ assertTrue(
+ vpnNc.hasAllTransports(defaultNetworkTransports),
+ "VPN transports ${Arrays.toString(vpnNc.transportTypes)}" +
+ " lacking transports from ${Arrays.toString(defaultNetworkTransports)}"
+ )
// Check that when no underlying networks are announced the underlying transport disappears.
agent.setUnderlyingNetworks(listOf<Network>())
@@ -999,9 +1030,11 @@
// underlying networks, and because not congested, not roaming, and not suspended are the
// default anyway. It's still useful as an extra check though.
vpnNc = mCM.getNetworkCapabilities(agent.network!!)!!
- for (cap in listOf(NET_CAPABILITY_NOT_CONGESTED,
- NET_CAPABILITY_NOT_ROAMING,
- NET_CAPABILITY_NOT_SUSPENDED)) {
+ for (cap in listOf(
+ NET_CAPABILITY_NOT_CONGESTED,
+ NET_CAPABILITY_NOT_ROAMING,
+ NET_CAPABILITY_NOT_SUSPENDED
+ )) {
val capStr = valueToString(NetworkCapabilities::class.java, "NET_CAPABILITY_", cap)
if (defaultNetworkCapabilities.hasCapability(cap) && !vpnNc.hasCapability(cap)) {
fail("$capStr not propagated from underlying: $defaultNetworkCapabilities")
@@ -1026,13 +1059,15 @@
doReturn(mockCm).`when`(mockContext).getSystemService(Context.CONNECTIVITY_SERVICE)
val agent = createNetworkAgent(mockContext)
agent.register()
- verify(mockCm).registerNetworkAgent(any(),
- argThat<NetworkInfo> { it.detailedState == NetworkInfo.DetailedState.CONNECTING },
- any(LinkProperties::class.java),
- any(NetworkCapabilities::class.java),
- any(NetworkScore::class.java),
- any(NetworkAgentConfig::class.java),
- eq(NetworkProvider.ID_NONE))
+ verify(mockCm).registerNetworkAgent(
+ any(),
+ argThat<NetworkInfo> { it.detailedState == NetworkInfo.DetailedState.CONNECTING },
+ any(LinkProperties::class.java),
+ any(NetworkCapabilities::class.java),
+ any(NetworkScore::class.java),
+ any(NetworkAgentConfig::class.java),
+ eq(NetworkProvider.ID_NONE)
+ )
}
@Test
@@ -1079,8 +1114,10 @@
@Test
fun testValidationStatus() = createNetworkAgentWithFakeCS().let { agent ->
val uri = Uri.parse("http://www.google.com")
- mFakeConnectivityService.agent.onValidationStatusChanged(VALID_NETWORK,
- uri.toString())
+ mFakeConnectivityService.agent.onValidationStatusChanged(
+ VALID_NETWORK,
+ uri.toString()
+ )
agent.expectCallback<OnValidationStatus>().let {
assertEquals(it.status, VALID_NETWORK)
assertEquals(it.uri, uri)
@@ -1155,7 +1192,8 @@
}
assertFailsWith<IllegalArgumentException> {
agentWeaker.setLingerDuration(Duration.ofMillis(
- NetworkAgent.MIN_LINGER_TIMER_MS.toLong() - 1))
+ NetworkAgent.MIN_LINGER_TIMER_MS.toLong() - 1
+ ))
}
// Verify valid linger timer can be set, but it should not take effect since the network
// is still needed.
@@ -1165,11 +1203,14 @@
agentWeaker.setLingerDuration(Duration.ofMillis(NetworkAgent.MIN_LINGER_TIMER_MS.toLong()))
// Make a listener which can observe agentWeaker lost later.
val callbackWeaker = TestableNetworkCallback(timeoutMs = DEFAULT_TIMEOUT_MS)
- registerNetworkCallback(NetworkRequest.Builder()
+ registerNetworkCallback(
+ NetworkRequest.Builder()
.clearCapabilities()
.addTransportType(TRANSPORT_TEST)
.setNetworkSpecifier(CompatUtil.makeEthernetNetworkSpecifier(specifierWeaker))
- .build(), callbackWeaker)
+ .build(),
+ callbackWeaker
+ )
callbackWeaker.expectAvailableCallbacks(agentWeaker.network!!)
// Connect the agentStronger with a score better than agentWeaker. Verify the callback for
@@ -1186,8 +1227,10 @@
val expectedRemainingLingerDuration = lingerStart +
NetworkAgent.MIN_LINGER_TIMER_MS.toLong() - SystemClock.elapsedRealtime()
// If the available callback is too late. The remaining duration will be reduced.
- assertTrue(expectedRemainingLingerDuration > 0,
- "expected remaining linger duration is $expectedRemainingLingerDuration")
+ assertTrue(
+ expectedRemainingLingerDuration > 0,
+ "expected remaining linger duration is $expectedRemainingLingerDuration"
+ )
callbackWeaker.assertNoCallback(expectedRemainingLingerDuration)
callbackWeaker.expect<Lost>(agentWeaker.network!!)
}
@@ -1205,20 +1248,24 @@
assertEquals(imsi, testNetworkSnapshot!!.subscriberId)
}
- @Test
- @IgnoreUpTo(Build.VERSION_CODES.R)
// TODO: Refactor helper functions to util class and move this test case to
// {@link android.net.cts.ConnectivityManagerTest}.
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.R)
fun testRegisterBestMatchingNetworkCallback() {
// Register best matching network callback with additional condition that will be
// exercised later. This assumes the test network agent has NOT_VCN_MANAGED in it and
// does not have NET_CAPABILITY_TEMPORARILY_NOT_METERED.
val bestMatchingCb = TestableNetworkCallback(timeoutMs = DEFAULT_TIMEOUT_MS)
- registerBestMatchingNetworkCallback(NetworkRequest.Builder()
+ registerBestMatchingNetworkCallback(
+ NetworkRequest.Builder()
.clearCapabilities()
.addTransportType(TRANSPORT_TEST)
.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
- .build(), bestMatchingCb, mHandlerThread.threadHandler)
+ .build(),
+ bestMatchingCb,
+ mHandlerThread.threadHandler
+ )
val (agent1, _) = createConnectedNetworkAgent(specifier = "AGENT-1")
bestMatchingCb.expectAvailableThenValidatedCallbacks(agent1.network!!)
@@ -1296,8 +1343,10 @@
}
fun assertNoCallback() {
- assertNull(history.poll(NO_CALLBACK_TIMEOUT),
- "Callback received")
+ assertNull(
+ history.poll(NO_CALLBACK_TIMEOUT),
+ "Callback received"
+ )
}
}
@@ -1312,7 +1361,8 @@
private fun setupForQosDatagram() = setupForQosCallbackTest {
agent: TestableNetworkAgent -> DatagramSocket(
- InetSocketAddress(InetAddress.getLoopbackAddress(), 0))
+ InetSocketAddress(InetAddress.getLoopbackAddress(), 0)
+ )
.also { assertNotNull(agent.network?.bindSocket(it)) }
}
@@ -1345,7 +1395,8 @@
assertFailsWith<QosCallbackRegistrationException>(
"The same callback cannot be " +
- "registered more than once without first being unregistered") {
+ "registered more than once without first being unregistered"
+ ) {
mCM.registerQosCallback(info, executor, qosCallback)
}
} finally {
@@ -1438,8 +1489,10 @@
qosCallback.expectCallback<OnQosSessionAvailable>()
// Check that onError is coming through correctly
- agent.sendQosCallbackError(callbackId,
- QosCallbackException.EX_TYPE_FILTER_NOT_SUPPORTED)
+ agent.sendQosCallbackError(
+ callbackId,
+ QosCallbackException.EX_TYPE_FILTER_NOT_SUPPORTED
+ )
qosCallback.expectCallback<OnError> {
it.ex.cause is UnsupportedOperationException
}
@@ -1534,13 +1587,20 @@
val remoteAddresses = ArrayList<InetSocketAddress>()
remoteAddresses.add(InetSocketAddress(REMOTE_ADDRESS, 80))
return EpsBearerQosSessionAttributes(
- qci, 2, 3, 4, 5,
- remoteAddresses
+ qci,
+ 2,
+ 3,
+ 4,
+ 5,
+ remoteAddresses
)
}
- fun sendAndExpectUdpPacket(net: Network,
- reader: PollPacketReader, iface: TestNetworkInterface) {
+ fun sendAndExpectUdpPacket(
+ net: Network,
+ reader: PollPacketReader,
+ iface: TestNetworkInterface
+ ) {
val s = Os.socket(AF_INET6, SOCK_DGRAM, 0)
net.bindSocket(s)
val content = ByteArray(16)
@@ -1553,8 +1613,11 @@
it[IPV6_PROTOCOL_OFFSET].toInt() == IPPROTO_UDP &&
Arrays.equals(content, it.copyOfRange(udpStart, udpStart + content.size))
}
- assertNotNull(match, "Did not receive matching packet on ${iface.interfaceName} " +
- " after ${DEFAULT_TIMEOUT_MS}ms")
+ assertNotNull(
+ match,
+ "Did not receive matching packet on ${iface.interfaceName} " +
+ " after ${DEFAULT_TIMEOUT_MS}ms"
+ )
}
fun createInterfaceAndReader(): Triple<TestNetworkInterface, PollPacketReader, LinkProperties> {
@@ -1750,8 +1813,10 @@
assertNotNull(wifiSpecifier)
assertTrue(wifiSpecifier is EthernetNetworkSpecifier)
- val wifiNc = makeTestNetworkCapabilities(wifiSpecifier.interfaceName,
- intArrayOf(TRANSPORT_WIFI))
+ val wifiNc = makeTestNetworkCapabilities(
+ wifiSpecifier.interfaceName,
+ intArrayOf(TRANSPORT_WIFI)
+ )
wifiAgent.sendNetworkCapabilities(wifiNc)
val wifiLp = mCM.getLinkProperties(wifiNetwork)!!
val newRoute = RouteInfo(IpPrefix("192.0.2.42/24"))
@@ -1822,8 +1887,12 @@
val nc = makeTestNetworkCapabilities(ifName, transports).also {
if (transports.contains(TRANSPORT_VPN)) {
val sessionId = "NetworkAgentTest-${Process.myPid()}"
- it.setTransportInfo(VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, sessionId,
- /*bypassable=*/ false, /*longLivedTcpConnectionsExpensive=*/ false))
+ it.setTransportInfo(VpnTransportInfo(
+ VpnManager.TYPE_VPN_PLATFORM,
+ sessionId,
+ /*bypassable=*/ false,
+ /*longLivedTcpConnectionsExpensive=*/ false
+ ))
it.underlyingNetworks = listOf()
}
}
@@ -1868,9 +1937,11 @@
listenCallback.expect<Available>(network)
requestCallback.expect<CapabilitiesChanged>(network) { it.caps.hasCapability(
- NET_CAPABILITY_TEMPORARILY_NOT_METERED) }
+ NET_CAPABILITY_TEMPORARILY_NOT_METERED
+ ) }
listenCallback.expect<CapabilitiesChanged>(network) { it.caps.hasCapability(
- NET_CAPABILITY_TEMPORARILY_NOT_METERED) }
+ NET_CAPABILITY_TEMPORARILY_NOT_METERED
+ ) }
requestCallback.expect<LinkPropertiesChanged>(network) { it.lp.equals(lp) }
listenCallback.expect<LinkPropertiesChanged>(network) { it.lp.equals(lp) }
@@ -1896,7 +1967,8 @@
fun testNativeNetworkCreation_PhysicalNetwork() {
doTestNativeNetworkCreation(
expectCreatedImmediately = SHOULD_CREATE_NETWORKS_IMMEDIATELY,
- intArrayOf(TRANSPORT_CELLULAR))
+ intArrayOf(TRANSPORT_CELLULAR)
+ )
}
@Test
diff --git a/tests/cts/net/src/android/net/cts/TrafficStatsTest.java b/tests/cts/net/src/android/net/cts/TrafficStatsTest.java
index bd9e03c..f5198e3 100755
--- a/tests/cts/net/src/android/net/cts/TrafficStatsTest.java
+++ b/tests/cts/net/src/android/net/cts/TrafficStatsTest.java
@@ -24,6 +24,8 @@
import android.util.Log;
import android.util.Range;
+import com.android.testutils.ConnectivityDiagnosticsCollector;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -94,11 +96,12 @@
long tcpPacketToIpBytes(long packetCount, long bytes) {
// ip header + tcp header + data.
- // Tcp header is mostly 32. Syn has different tcp options -> 40. Don't care.
+ // Tcp header is mostly 32. Syn has different tcp options -> 40.
return packetCount * (20 + 32 + bytes);
}
@AppModeFull(reason = "Socket cannot bind in instant app mode")
+ @ConnectivityDiagnosticsCollector.CollectTcpdumpOnFailure
public void testTrafficStatsForLocalhost() throws IOException {
final long mobileTxPacketsBefore = TrafficStats.getMobileTxPackets();
final long mobileRxPacketsBefore = TrafficStats.getMobileRxPackets();
@@ -224,9 +227,15 @@
- uidTxDeltaPackets;
final long deltaRxOtherPackets = (totalRxPacketsAfter - totalRxPacketsBefore)
- uidRxDeltaPackets;
- if (deltaTxOtherPackets > 0 || deltaRxOtherPackets > 0) {
+ final long deltaTxOtherPktBytes = (totalTxBytesAfter - totalTxBytesBefore)
+ - uidTxDeltaBytes;
+ final long deltaRxOtherPktBytes = (totalRxBytesAfter - totalRxBytesBefore)
+ - uidRxDeltaBytes;
+ if (deltaTxOtherPackets != 0 || deltaRxOtherPackets != 0
+ || deltaTxOtherPktBytes != 0 || deltaRxOtherPktBytes != 0) {
Log.i(LOG_TAG, "lingering traffic data: " + deltaTxOtherPackets + "/"
- + deltaRxOtherPackets);
+ + deltaRxOtherPackets + "/" + deltaTxOtherPktBytes
+ + "/" + deltaRxOtherPktBytes);
}
// Check that the per-uid stats obtained from data profiling contain the expected values.
@@ -237,9 +246,9 @@
final long pktBytes = tcpPacketToIpBytes(packetCount, byteCount);
final long pktWithNoDataBytes = tcpPacketToIpBytes(packetCount, 0);
final long minExpExtraPktBytes = tcpPacketToIpBytes(minExpectedExtraPackets, 0);
- final long maxExpExtraPktBytes = tcpPacketToIpBytes(maxExpectedExtraPackets, 0);
- final long deltaTxOtherPktBytes = tcpPacketToIpBytes(deltaTxOtherPackets, 0);
- final long deltaRxOtherPktBytes = tcpPacketToIpBytes(deltaRxOtherPackets, 0);
+ // Syn/syn-ack has different tcp options, make tcp header 40 for upper bound estimation.
+ final long maxExpExtraPktBytes = tcpPacketToIpBytes(maxExpectedExtraPackets, 8);
+
assertInRange("txPackets detail", entry.txPackets, packetCount + minExpectedExtraPackets,
uidTxDeltaPackets);
assertInRange("rxPackets detail", entry.rxPackets, packetCount + minExpectedExtraPackets,
@@ -257,32 +266,24 @@
assertInRange("uidrxb", uidRxDeltaBytes, pktBytes + minExpExtraPktBytes,
pktBytes + pktWithNoDataBytes + maxExpExtraPktBytes + deltaRxOtherPktBytes);
assertInRange("iftxp", ifaceTxDeltaPackets, packetCount + minExpectedExtraPackets,
- packetCount + packetCount + maxExpectedExtraPackets);
+ packetCount + packetCount + maxExpectedExtraPackets + deltaTxOtherPackets);
assertInRange("ifrxp", ifaceRxDeltaPackets, packetCount + minExpectedExtraPackets,
- packetCount + packetCount + maxExpectedExtraPackets);
+ packetCount + packetCount + maxExpectedExtraPackets + deltaRxOtherPackets);
assertInRange("iftxb", ifaceTxDeltaBytes, pktBytes + minExpExtraPktBytes,
- pktBytes + pktWithNoDataBytes + maxExpExtraPktBytes);
+ pktBytes + pktWithNoDataBytes + maxExpExtraPktBytes + deltaTxOtherPktBytes);
assertInRange("ifrxb", ifaceRxDeltaBytes, pktBytes + minExpExtraPktBytes,
- pktBytes + pktWithNoDataBytes + maxExpExtraPktBytes);
+ pktBytes + pktWithNoDataBytes + maxExpExtraPktBytes + deltaRxOtherPktBytes);
// Localhost traffic *does* count against total stats.
// Check the total stats increased after test data transfer over localhost has been made.
- assertTrue("ttxp: " + totalTxPacketsBefore + " -> " + totalTxPacketsAfter,
- totalTxPacketsAfter >= totalTxPacketsBefore + uidTxDeltaPackets);
- assertTrue("trxp: " + totalRxPacketsBefore + " -> " + totalRxPacketsAfter,
- totalRxPacketsAfter >= totalRxPacketsBefore + uidRxDeltaPackets);
- assertTrue("ttxb: " + totalTxBytesBefore + " -> " + totalTxBytesAfter,
- totalTxBytesAfter >= totalTxBytesBefore + uidTxDeltaBytes);
- assertTrue("trxb: " + totalRxBytesBefore + " -> " + totalRxBytesAfter,
- totalRxBytesAfter >= totalRxBytesBefore + uidRxDeltaBytes);
- assertTrue("iftxp: " + ifaceTxPacketsBefore + " -> " + ifaceTxPacketsAfter,
- totalTxPacketsAfter >= totalTxPacketsBefore + ifaceTxDeltaPackets);
- assertTrue("ifrxp: " + ifaceRxPacketsBefore + " -> " + ifaceRxPacketsAfter,
- totalRxPacketsAfter >= totalRxPacketsBefore + ifaceRxDeltaPackets);
- assertTrue("iftxb: " + ifaceTxBytesBefore + " -> " + ifaceTxBytesAfter,
- totalTxBytesAfter >= totalTxBytesBefore + ifaceTxDeltaBytes);
- assertTrue("ifrxb: " + ifaceRxBytesBefore + " -> " + ifaceRxBytesAfter,
- totalRxBytesAfter >= totalRxBytesBefore + ifaceRxDeltaBytes);
+ assertInRange("ttxp", totalTxPacketsAfter,
+ totalTxPacketsBefore + packetCount + minExpectedExtraPackets, Long.MAX_VALUE);
+ assertInRange("trxp", totalRxPacketsAfter,
+ totalRxPacketsBefore + packetCount + minExpectedExtraPackets, Long.MAX_VALUE);
+ assertInRange("ttxb", totalTxBytesAfter,
+ totalTxBytesBefore + pktBytes + minExpExtraPktBytes, Long.MAX_VALUE);
+ assertInRange("trxb", totalRxBytesAfter,
+ totalRxBytesBefore + pktBytes + minExpExtraPktBytes, Long.MAX_VALUE);
// Localhost traffic should *not* count against mobile stats,
// There might be some other traffic, but nowhere near 1MB.
diff --git a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
index 2420026..d103f75 100644
--- a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
+++ b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
@@ -88,9 +88,11 @@
import com.android.modules.utils.build.SdkLevel;
import com.android.net.flags.Flags;
import com.android.testutils.ParcelUtils;
+import com.android.testutils.com.android.testutils.CarrierConfigRule;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -106,6 +108,8 @@
@RunWith(AndroidJUnit4.class)
public class TetheringManagerTest {
+ @Rule
+ public final CarrierConfigRule mCarrierConfigRule = new CarrierConfigRule();
private Context mContext;
@@ -551,22 +555,13 @@
// Override carrier config to ignore entitlement check.
final PersistableBundle bundle = new PersistableBundle();
bundle.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, false);
- overrideCarrierConfig(bundle);
+ mCarrierConfigRule.addConfigOverrides(
+ SubscriptionManager.getDefaultSubscriptionId(), bundle);
// Verify that requestLatestTetheringEntitlementResult() can get entitlement
// result TETHER_ERROR_NO_ERROR due to provisioning bypassed.
assertEntitlementResult(listener -> mTM.requestLatestTetheringEntitlementResult(
TETHERING_WIFI, false, c -> c.run(), listener), TETHER_ERROR_NO_ERROR);
-
- // Reset carrier config.
- overrideCarrierConfig(null);
- }
-
- private void overrideCarrierConfig(PersistableBundle bundle) {
- final CarrierConfigManager configManager = (CarrierConfigManager) mContext
- .getSystemService(Context.CARRIER_CONFIG_SERVICE);
- final int subId = SubscriptionManager.getDefaultSubscriptionId();
- runAsShell(MODIFY_PHONE_STATE, () -> configManager.overrideConfig(subId, bundle));
}
private boolean isTetheringApnRequired() {