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() {