Merge changes from topic "switch_uid_owner_map_api"

* changes:
  Switch previous and new code path in replaceUidChain
  Switch previous and new code path in setUidRule
  Switch previous and new code path in removeUidInterfaceRules
  Switch previous and new code path in addUidInterfaceRules
  Switch previous and new code path in updateUidLockdownRule
  Switch previous and new code path in addNiceApp
  Switch previous and new code path in removeNiceApp
  Switch previous and new code path in addNaughtyApp
  Switch previous and new code path in removeNaughtyApp
diff --git a/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java b/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java
index 18ef631..898b124 100644
--- a/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java
+++ b/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java
@@ -168,13 +168,13 @@
     }
 
     @Override
-    public boolean attachProgram(String iface, boolean downstream) {
+    public boolean attachProgram(String iface, boolean downstream, boolean ipv4) {
         /* no op */
         return true;
     }
 
     @Override
-    public boolean detachProgram(String iface) {
+    public boolean detachProgram(String iface, boolean ipv4) {
         /* no op */
         return true;
     }
diff --git a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
index fd9dab5..776832f 100644
--- a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
+++ b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
@@ -425,11 +425,11 @@
     }
 
     @Override
-    public boolean attachProgram(String iface, boolean downstream) {
+    public boolean attachProgram(String iface, boolean downstream, boolean ipv4) {
         if (!isInitialized()) return false;
 
         try {
-            BpfUtils.attachProgram(iface, downstream);
+            BpfUtils.attachProgram(iface, downstream, ipv4);
         } catch (IOException e) {
             mLog.e("Could not attach program: " + e);
             return false;
@@ -438,11 +438,11 @@
     }
 
     @Override
-    public boolean detachProgram(String iface) {
+    public boolean detachProgram(String iface, boolean ipv4) {
         if (!isInitialized()) return false;
 
         try {
-            BpfUtils.detachProgram(iface);
+            BpfUtils.detachProgram(iface, ipv4);
         } catch (IOException e) {
             mLog.e("Could not detach program: " + e);
             return false;
diff --git a/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java b/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
index 69cbab5..51cecfe 100644
--- a/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
+++ b/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
@@ -172,16 +172,24 @@
     /**
      * Attach BPF program.
      *
+     * @param iface the interface name to attach program.
+     * @param downstream indicate the datapath. true if downstream, false if upstream.
+     * @param ipv4 indicate the protocol family. true if ipv4, false if ipv6.
+     *
      * TODO: consider using InterfaceParams to replace interface name.
      */
-    public abstract boolean attachProgram(@NonNull String iface, boolean downstream);
+    public abstract boolean attachProgram(@NonNull String iface, boolean downstream,
+            boolean ipv4);
 
     /**
      * Detach BPF program.
      *
+     * @param iface the interface name to detach program.
+     * @param ipv4 indicate the protocol family. true if ipv4, false if ipv6.
+     *
      * TODO: consider using InterfaceParams to replace interface name.
      */
-    public abstract boolean detachProgram(@NonNull String iface);
+    public abstract boolean detachProgram(@NonNull String iface, boolean ipv4);
 
     /**
      * Add interface index mapping.
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 49442a6..7e0a589 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -895,6 +895,28 @@
         }
     }
 
+    private boolean is464XlatInterface(@NonNull String ifaceName) {
+        return ifaceName.startsWith("v4-");
+    }
+
+    private void maybeAttachProgramImpl(@NonNull String iface, boolean downstream) {
+        mBpfCoordinatorShim.attachProgram(iface, downstream, true /* ipv4 */);
+
+        // Ignore 464xlat interface because it is IPv4 only.
+        if (!is464XlatInterface(iface)) {
+            mBpfCoordinatorShim.attachProgram(iface, downstream, false /* ipv4 */);
+        }
+    }
+
+    private void maybeDetachProgramImpl(@NonNull String iface) {
+        mBpfCoordinatorShim.detachProgram(iface, true /* ipv4 */);
+
+        // Ignore 464xlat interface because it is IPv4 only.
+        if (!is464XlatInterface(iface)) {
+            mBpfCoordinatorShim.detachProgram(iface, false /* ipv4 */);
+        }
+    }
+
     /**
      * Attach BPF program
      *
@@ -905,13 +927,19 @@
 
         if (forwardingPairExists(intIface, extIface)) return;
 
+        boolean firstUpstreamForThisDownstream = !isAnyForwardingPairOnDownstream(intIface);
         boolean firstDownstreamForThisUpstream = !isAnyForwardingPairOnUpstream(extIface);
         forwardingPairAdd(intIface, extIface);
 
-        mBpfCoordinatorShim.attachProgram(intIface, UPSTREAM);
+        // Attach if the downstream is the first time to be used in a forwarding pair.
+        // Ex: IPv6 only interface has two forwarding pair, iface and v4-iface, on the
+        // same downstream.
+        if (firstUpstreamForThisDownstream) {
+            maybeAttachProgramImpl(intIface, UPSTREAM);
+        }
         // Attach if the upstream is the first time to be used in a forwarding pair.
         if (firstDownstreamForThisUpstream) {
-            mBpfCoordinatorShim.attachProgram(extIface, DOWNSTREAM);
+            maybeAttachProgramImpl(extIface, DOWNSTREAM);
         }
     }
 
@@ -922,10 +950,12 @@
         forwardingPairRemove(intIface, extIface);
 
         // Detaching program may fail because the interface has been removed already.
-        mBpfCoordinatorShim.detachProgram(intIface);
+        if (!isAnyForwardingPairOnDownstream(intIface)) {
+            maybeDetachProgramImpl(intIface);
+        }
         // Detach if no more forwarding pair is using the upstream.
         if (!isAnyForwardingPairOnUpstream(extIface)) {
-            mBpfCoordinatorShim.detachProgram(extIface);
+            maybeDetachProgramImpl(extIface);
         }
     }
 
@@ -1827,6 +1857,13 @@
         return mForwardingPairs.containsKey(extIface);
     }
 
+    private boolean isAnyForwardingPairOnDownstream(@NonNull String intIface) {
+        for (final HashSet downstreams : mForwardingPairs.values()) {
+            if (downstreams.contains(intIface)) return true;
+        }
+        return false;
+    }
+
     @NonNull
     private NetworkStats buildNetworkStats(@NonNull StatsType type, int ifIndex,
             @NonNull final ForwardedStats diff) {
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfUtils.java b/Tethering/src/com/android/networkstack/tethering/BpfUtils.java
index 3d2dfaa..12a0c96 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfUtils.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfUtils.java
@@ -74,7 +74,7 @@
      *
      * TODO: use interface index to replace interface name.
      */
-    public static void attachProgram(@NonNull String iface, boolean downstream)
+    public static void attachProgram(@NonNull String iface, boolean downstream, boolean ipv4)
             throws IOException {
         final InterfaceParams params = InterfaceParams.getByName(iface);
         if (params == null) {
@@ -88,24 +88,26 @@
             throw new IOException("isEthernet(" + params.index + "[" + iface + "]) failure: " + e);
         }
 
-        try {
-            // tc filter add dev .. ingress prio 1 protocol ipv6 bpf object-pinned /sys/fs/bpf/...
-            // direct-action
-            TcUtils.tcFilterAddDevBpf(params.index, INGRESS, PRIO_TETHER6, (short) ETH_P_IPV6,
-                    makeProgPath(downstream, 6, ether));
-        } catch (IOException e) {
-            throw new IOException("tc filter add dev (" + params.index + "[" + iface
-                    + "]) ingress prio PRIO_TETHER6 protocol ipv6 failure: " + e);
-        }
-
-        try {
-            // tc filter add dev .. ingress prio 2 protocol ip bpf object-pinned /sys/fs/bpf/...
-            // direct-action
-            TcUtils.tcFilterAddDevBpf(params.index, INGRESS, PRIO_TETHER4, (short) ETH_P_IP,
-                    makeProgPath(downstream, 4, ether));
-        } catch (IOException e) {
-            throw new IOException("tc filter add dev (" + params.index + "[" + iface
-                    + "]) ingress prio PRIO_TETHER4 protocol ip failure: " + e);
+        if (ipv4) {
+            try {
+                // tc filter add dev .. ingress prio 2 protocol ip bpf object-pinned /sys/fs/bpf/...
+                // direct-action
+                TcUtils.tcFilterAddDevBpf(params.index, INGRESS, PRIO_TETHER4, (short) ETH_P_IP,
+                        makeProgPath(downstream, 4, ether));
+            } catch (IOException e) {
+                throw new IOException("tc filter add dev (" + params.index + "[" + iface
+                        + "]) ingress prio PRIO_TETHER4 protocol ip failure: " + e);
+            }
+        } else {
+            try {
+                // tc filter add dev .. ingress prio 1 protocol ipv6 bpf object-pinned
+                // /sys/fs/bpf/... direct-action
+                TcUtils.tcFilterAddDevBpf(params.index, INGRESS, PRIO_TETHER6, (short) ETH_P_IPV6,
+                        makeProgPath(downstream, 6, ether));
+            } catch (IOException e) {
+                throw new IOException("tc filter add dev (" + params.index + "[" + iface
+                        + "]) ingress prio PRIO_TETHER6 protocol ipv6 failure: " + e);
+            }
         }
     }
 
@@ -114,26 +116,28 @@
      *
      * TODO: use interface index to replace interface name.
      */
-    public static void detachProgram(@NonNull String iface) throws IOException {
+    public static void detachProgram(@NonNull String iface, boolean ipv4) throws IOException {
         final InterfaceParams params = InterfaceParams.getByName(iface);
         if (params == null) {
             throw new IOException("Fail to get interface params for interface " + iface);
         }
 
-        try {
-            // tc filter del dev .. ingress prio 1 protocol ipv6
-            TcUtils.tcFilterDelDev(params.index, INGRESS, PRIO_TETHER6, (short) ETH_P_IPV6);
-        } catch (IOException e) {
-            throw new IOException("tc filter del dev (" + params.index + "[" + iface
-                    + "]) ingress prio PRIO_TETHER6 protocol ipv6 failure: " + e);
-        }
-
-        try {
-            // tc filter del dev .. ingress prio 2 protocol ip
-            TcUtils.tcFilterDelDev(params.index, INGRESS, PRIO_TETHER4, (short) ETH_P_IP);
-        } catch (IOException e) {
-            throw new IOException("tc filter del dev (" + params.index + "[" + iface
-                    + "]) ingress prio PRIO_TETHER4 protocol ip failure: " + e);
+        if (ipv4) {
+            try {
+                // tc filter del dev .. ingress prio 2 protocol ip
+                TcUtils.tcFilterDelDev(params.index, INGRESS, PRIO_TETHER4, (short) ETH_P_IP);
+            } catch (IOException e) {
+                throw new IOException("tc filter del dev (" + params.index + "[" + iface
+                        + "]) ingress prio PRIO_TETHER4 protocol ip failure: " + e);
+            }
+        } else {
+            try {
+                // tc filter del dev .. ingress prio 1 protocol ipv6
+                TcUtils.tcFilterDelDev(params.index, INGRESS, PRIO_TETHER6, (short) ETH_P_IPV6);
+            } catch (IOException e) {
+                throw new IOException("tc filter del dev (" + params.index + "[" + iface
+                        + "]) ingress prio PRIO_TETHER6 protocol ipv6 failure: " + e);
+            }
         }
     }
 }
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 0c59b61..75f63c8 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -2772,7 +2772,8 @@
         // If we don't care about this type of interface, ignore.
         final int interfaceType = ifaceNameToType(iface);
         if (!checkTetherableType(interfaceType)) {
-            mLog.log(iface + " is used for " + interfaceType + " which is not tetherable");
+            mLog.log(iface + " is used for " + interfaceType + " which is not tetherable"
+                     + " (-1 == INVALID is expected on upstream interface)");
             return;
         }
 
diff --git a/Tethering/tests/integration/Android.bp b/Tethering/tests/integration/Android.bp
index ca8d3de..9aa2cff 100644
--- a/Tethering/tests/integration/Android.bp
+++ b/Tethering/tests/integration/Android.bp
@@ -79,7 +79,6 @@
     defaults: ["TetheringIntegrationTestsDefaults"],
     test_suites: [
         "device-tests",
-        "mts-tethering",
     ],
     compile_multilib: "both",
     jarjar_rules: ":NetworkStackJarJarRules",
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
index fa1d881..63bb731 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -59,6 +59,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.anyString;
@@ -141,6 +142,9 @@
     @Rule
     public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
 
+    private static final boolean IPV4 = true;
+    private static final boolean IPV6 = false;
+
     private static final int TEST_NET_ID = 24;
     private static final int TEST_NET_ID2 = 25;
 
@@ -1277,48 +1281,72 @@
         try {
             final String intIface1 = "wlan1";
             final String intIface2 = "rndis0";
-            final String extIface = "rmnet_data0";
+            final String extIface1 = "rmnet_data0";
+            final String extIface2 = "v4-rmnet_data0";
             final String virtualIface = "ipsec0";
             final BpfUtils mockMarkerBpfUtils = staticMockMarker(BpfUtils.class);
             final BpfCoordinator coordinator = makeBpfCoordinator();
 
             // [1] Add the forwarding pair <wlan1, rmnet_data0>. Expect that attach both wlan1 and
             // rmnet_data0.
-            coordinator.maybeAttachProgram(intIface1, extIface);
-            ExtendedMockito.verify(() -> BpfUtils.attachProgram(extIface, DOWNSTREAM));
-            ExtendedMockito.verify(() -> BpfUtils.attachProgram(intIface1, UPSTREAM));
+            coordinator.maybeAttachProgram(intIface1, extIface1);
+            ExtendedMockito.verify(() -> BpfUtils.attachProgram(extIface1, DOWNSTREAM, IPV4));
+            ExtendedMockito.verify(() -> BpfUtils.attachProgram(extIface1, DOWNSTREAM, IPV6));
+            ExtendedMockito.verify(() -> BpfUtils.attachProgram(intIface1, UPSTREAM, IPV4));
+            ExtendedMockito.verify(() -> BpfUtils.attachProgram(intIface1, UPSTREAM, IPV6));
             ExtendedMockito.verifyNoMoreInteractions(mockMarkerBpfUtils);
             ExtendedMockito.clearInvocations(mockMarkerBpfUtils);
 
             // [2] Add the forwarding pair <wlan1, rmnet_data0> again. Expect no more action.
-            coordinator.maybeAttachProgram(intIface1, extIface);
+            coordinator.maybeAttachProgram(intIface1, extIface1);
             ExtendedMockito.verifyNoMoreInteractions(mockMarkerBpfUtils);
             ExtendedMockito.clearInvocations(mockMarkerBpfUtils);
 
             // [3] Add the forwarding pair <rndis0, rmnet_data0>. Expect that attach rndis0 only.
-            coordinator.maybeAttachProgram(intIface2, extIface);
-            ExtendedMockito.verify(() -> BpfUtils.attachProgram(intIface2, UPSTREAM));
+            coordinator.maybeAttachProgram(intIface2, extIface1);
+            ExtendedMockito.verify(() -> BpfUtils.attachProgram(intIface2, UPSTREAM, IPV4));
+            ExtendedMockito.verify(() -> BpfUtils.attachProgram(intIface2, UPSTREAM, IPV6));
             ExtendedMockito.verifyNoMoreInteractions(mockMarkerBpfUtils);
             ExtendedMockito.clearInvocations(mockMarkerBpfUtils);
 
-            // [4] Remove the forwarding pair <rndis0, rmnet_data0>. Expect detach rndis0 only.
-            coordinator.maybeDetachProgram(intIface2, extIface);
-            ExtendedMockito.verify(() -> BpfUtils.detachProgram(intIface2));
+            // [4] Add the forwarding pair <rndis0, v4-rmnet_data0>. Expect that attach
+            // v4-rmnet_data0 IPv4 program only.
+            coordinator.maybeAttachProgram(intIface2, extIface2);
+            ExtendedMockito.verify(() -> BpfUtils.attachProgram(extIface2, DOWNSTREAM, IPV4));
+            ExtendedMockito.verify(() -> BpfUtils.attachProgram(extIface2, DOWNSTREAM, IPV6),
+                    never());
             ExtendedMockito.verifyNoMoreInteractions(mockMarkerBpfUtils);
             ExtendedMockito.clearInvocations(mockMarkerBpfUtils);
 
-            // [5] Remove the forwarding pair <wlan1, rmnet_data0>. Expect that detach both wlan1
+            // [5] Remove the forwarding pair <rndis0, v4-rmnet_data0>. Expect detach
+            // v4-rmnet_data0 IPv4 program only.
+            coordinator.maybeDetachProgram(intIface2, extIface2);
+            ExtendedMockito.verify(() -> BpfUtils.detachProgram(extIface2, IPV4));
+            ExtendedMockito.verify(() -> BpfUtils.detachProgram(extIface2, IPV6), never());
+            ExtendedMockito.verifyNoMoreInteractions(mockMarkerBpfUtils);
+            ExtendedMockito.clearInvocations(mockMarkerBpfUtils);
+
+            // [6] Remove the forwarding pair <rndis0, rmnet_data0>. Expect detach rndis0 only.
+            coordinator.maybeDetachProgram(intIface2, extIface1);
+            ExtendedMockito.verify(() -> BpfUtils.detachProgram(intIface2, IPV4));
+            ExtendedMockito.verify(() -> BpfUtils.detachProgram(intIface2, IPV6));
+            ExtendedMockito.verifyNoMoreInteractions(mockMarkerBpfUtils);
+            ExtendedMockito.clearInvocations(mockMarkerBpfUtils);
+
+            // [7] Remove the forwarding pair <wlan1, rmnet_data0>. Expect that detach both wlan1
             // and rmnet_data0.
-            coordinator.maybeDetachProgram(intIface1, extIface);
-            ExtendedMockito.verify(() -> BpfUtils.detachProgram(extIface));
-            ExtendedMockito.verify(() -> BpfUtils.detachProgram(intIface1));
+            coordinator.maybeDetachProgram(intIface1, extIface1);
+            ExtendedMockito.verify(() -> BpfUtils.detachProgram(extIface1, IPV4));
+            ExtendedMockito.verify(() -> BpfUtils.detachProgram(extIface1, IPV6));
+            ExtendedMockito.verify(() -> BpfUtils.detachProgram(intIface1, IPV4));
+            ExtendedMockito.verify(() -> BpfUtils.detachProgram(intIface1, IPV6));
             ExtendedMockito.verifyNoMoreInteractions(mockMarkerBpfUtils);
             ExtendedMockito.clearInvocations(mockMarkerBpfUtils);
 
-            // [6] Skip attaching if upstream is virtual interface.
+            // [8] Skip attaching if upstream is virtual interface.
             coordinator.maybeAttachProgram(intIface1, virtualIface);
-            ExtendedMockito.verify(() -> BpfUtils.attachProgram(extIface, DOWNSTREAM), never());
-            ExtendedMockito.verify(() -> BpfUtils.attachProgram(intIface1, UPSTREAM), never());
+            ExtendedMockito.verify(() ->
+                    BpfUtils.attachProgram(anyString(), anyBoolean(), anyBoolean()), never());
             ExtendedMockito.verifyNoMoreInteractions(mockMarkerBpfUtils);
             ExtendedMockito.clearInvocations(mockMarkerBpfUtils);
 
diff --git a/bpf_progs/clatd.c b/bpf_progs/clatd.c
index 66e9616..a2214dc 100644
--- a/bpf_progs/clatd.c
+++ b/bpf_progs/clatd.c
@@ -342,4 +342,4 @@
 }
 
 LICENSE("Apache 2.0");
-CRITICAL("netd");
+CRITICAL("Connectivity");
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index 44f76de..eb78cb3 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -432,4 +432,4 @@
 }
 
 LICENSE("Apache 2.0");
-CRITICAL("netd");
+CRITICAL("Connectivity and netd");
diff --git a/bpf_progs/offload.c b/bpf_progs/offload.c
index 4eb1e8d..898f2e2 100644
--- a/bpf_progs/offload.c
+++ b/bpf_progs/offload.c
@@ -320,50 +320,32 @@
 //   ANDROID: net: bpf: permit redirect from ingress L3 to egress L2 devices at near max mtu
 // (the first of those has already been upstreamed)
 //
-// 5.4 kernel support was only added to Android Common Kernel in R,
-// and thus a 5.4 kernel always supports this.
+// These were added to 4.14+ Android Common Kernel in R (including the original release of ACK 5.4)
+// and there is a test in kernel/tests/net/test/bpf_test.py testSkbChangeHead()
+// and in system/netd/tests/binder_test.cpp NetdBinderTest TetherOffloadForwarding.
 //
-// Hence, these mandatory (must load successfully) implementations for 5.4+ kernels:
-DEFINE_BPF_PROG_KVER("schedcls/tether_downstream6_rawip$5_4", TETHERING_UID, TETHERING_GID,
-                     sched_cls_tether_downstream6_rawip_5_4, KVER(5, 4, 0))
+// Hence, these mandatory (must load successfully) implementations for 4.14+ kernels:
+DEFINE_BPF_PROG_KVER("schedcls/tether_downstream6_rawip$4_14", TETHERING_UID, TETHERING_GID,
+                     sched_cls_tether_downstream6_rawip_4_14, KVER(4, 14, 0))
 (struct __sk_buff* skb) {
     return do_forward6(skb, /* is_ethernet */ false, /* downstream */ true);
 }
 
-DEFINE_BPF_PROG_KVER("schedcls/tether_upstream6_rawip$5_4", TETHERING_UID, TETHERING_GID,
-                     sched_cls_tether_upstream6_rawip_5_4, KVER(5, 4, 0))
+DEFINE_BPF_PROG_KVER("schedcls/tether_upstream6_rawip$4_14", TETHERING_UID, TETHERING_GID,
+                     sched_cls_tether_upstream6_rawip_4_14, KVER(4, 14, 0))
 (struct __sk_buff* skb) {
     return do_forward6(skb, /* is_ethernet */ false, /* downstream */ false);
 }
 
-// and these identical optional (may fail to load) implementations for [4.14..5.4) patched kernels:
-DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_downstream6_rawip$4_14",
-                                    TETHERING_UID, TETHERING_GID,
-                                    sched_cls_tether_downstream6_rawip_4_14,
-                                    KVER(4, 14, 0), KVER(5, 4, 0))
-(struct __sk_buff* skb) {
-    return do_forward6(skb, /* is_ethernet */ false, /* downstream */ true);
-}
-
-DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_upstream6_rawip$4_14",
-                                    TETHERING_UID, TETHERING_GID,
-                                    sched_cls_tether_upstream6_rawip_4_14,
-                                    KVER(4, 14, 0), KVER(5, 4, 0))
-(struct __sk_buff* skb) {
-    return do_forward6(skb, /* is_ethernet */ false, /* downstream */ false);
-}
-
-// and define no-op stubs for [4.9,4.14) and unpatched [4.14,5.4) kernels.
-// (if the above real 4.14+ program loaded successfully, then bpfloader will have already pinned
-// it at the same location this one would be pinned at and will thus skip loading this stub)
+// and define no-op stubs for pre-4.14 kernels.
 DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream6_rawip$stub", TETHERING_UID, TETHERING_GID,
-                           sched_cls_tether_downstream6_rawip_stub, KVER_NONE, KVER(5, 4, 0))
+                           sched_cls_tether_downstream6_rawip_stub, KVER_NONE, KVER(4, 14, 0))
 (struct __sk_buff* skb) {
     return TC_ACT_PIPE;
 }
 
 DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream6_rawip$stub", TETHERING_UID, TETHERING_GID,
-                           sched_cls_tether_upstream6_rawip_stub, KVER_NONE, KVER(5, 4, 0))
+                           sched_cls_tether_upstream6_rawip_stub, KVER_NONE, KVER(4, 14, 0))
 (struct __sk_buff* skb) {
     return TC_ACT_PIPE;
 }
@@ -882,4 +864,4 @@
 }
 
 LICENSE("Apache 2.0");
-CRITICAL("tethering");
+CRITICAL("Connectivity (Tethering)");
diff --git a/framework-t/Android.bp b/framework-t/Android.bp
index c76416f..c32742b 100644
--- a/framework-t/Android.bp
+++ b/framework-t/Android.bp
@@ -43,14 +43,9 @@
         ":framework-connectivity-tiramisu-updatable-sources",
         ":framework-nearby-java-sources",
     ],
-    stub_only_libs: [
-        // Use prebuilt framework-connectivity stubs to avoid circular dependencies
-        "sdk_module-lib_current_framework-connectivity",
-    ],
     libs: [
         "unsupportedappusage",
         "app-compat-annotations",
-        "sdk_module-lib_current_framework-connectivity",
     ],
     impl_only_libs: [
         // The build system will use framework-bluetooth module_current stubs, because
@@ -104,6 +99,13 @@
     // The jarjar rules are only so that references to jarjared utils in
     // framework-connectivity-pre-jarjar match at runtime.
     jarjar_rules: ":framework-connectivity-jarjar-rules",
+    stub_only_libs: [
+        // Use prebuilt framework-connectivity stubs to avoid circular dependencies
+        "sdk_module-lib_current_framework-connectivity",
+    ],
+    libs: [
+        "sdk_module-lib_current_framework-connectivity",
+    ],
     permitted_packages: [
         "android.app.usage",
         "android.net",
diff --git a/framework-t/src/android/net/NetworkStats.java b/framework-t/src/android/net/NetworkStats.java
index a655a9b..8719960 100644
--- a/framework-t/src/android/net/NetworkStats.java
+++ b/framework-t/src/android/net/NetworkStats.java
@@ -302,20 +302,8 @@
         /** @hide */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         public Entry() {
-            this(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
-        }
-
-        /** @hide */
-        public Entry(long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
-            this(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets,
-                    operations);
-        }
-
-        /** @hide */
-        public Entry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets,
-                long txBytes, long txPackets, long operations) {
-            this(iface, uid, set, tag, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
-                    rxBytes, rxPackets, txBytes, txPackets, operations);
+            this(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                    DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
         }
 
         /**
@@ -607,7 +595,8 @@
     public NetworkStats insertEntry(
             String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) {
         return insertEntry(
-                iface, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, 0L);
+                iface, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
+                rxBytes, rxPackets, txBytes, txPackets, 0L);
     }
 
     /** @hide */
@@ -615,7 +604,8 @@
     public NetworkStats insertEntry(String iface, int uid, int set, int tag, long rxBytes,
             long rxPackets, long txBytes, long txPackets, long operations) {
         return insertEntry(new Entry(
-                iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
+                iface, uid, set, tag,  METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
+                rxBytes, rxPackets, txBytes, txPackets, operations));
     }
 
     /** @hide */
@@ -787,7 +777,8 @@
     public NetworkStats combineValues(String iface, int uid, int set, int tag,
             long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
         return combineValues(new Entry(
-                iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
+                iface, uid, set, tag, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
+                rxBytes, rxPackets, txBytes, txPackets, operations));
     }
 
     /**
diff --git a/framework-t/src/android/net/NetworkStatsCollection.java b/framework-t/src/android/net/NetworkStatsCollection.java
index df42b58..e23faa4 100644
--- a/framework-t/src/android/net/NetworkStatsCollection.java
+++ b/framework-t/src/android/net/NetworkStatsCollection.java
@@ -309,7 +309,8 @@
             // ourselves something to scale with.
             if (entry.rxBytes == 0 || entry.txBytes == 0) {
                 combined.recordData(augmentStart, augmentEnd,
-                        new NetworkStats.Entry(1, 0, 1, 0, 0));
+                        new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 0L, 1L, 0L, 0L));
                 combined.getValues(augmentStart, augmentEnd, entry);
             }
 
diff --git a/framework-t/src/android/net/NetworkStatsHistory.java b/framework-t/src/android/net/NetworkStatsHistory.java
index 738e9cc..c345747 100644
--- a/framework-t/src/android/net/NetworkStatsHistory.java
+++ b/framework-t/src/android/net/NetworkStatsHistory.java
@@ -17,7 +17,10 @@
 package android.net;
 
 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
 import static android.net.NetworkStats.IFACE_ALL;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
 import static android.net.NetworkStats.SET_DEFAULT;
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
@@ -529,7 +532,8 @@
     @Deprecated
     public void recordData(long start, long end, long rxBytes, long txBytes) {
         recordData(start, end, new NetworkStats.Entry(
-                IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, 0L, txBytes, 0L, 0L));
+                IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, rxBytes, 0L, txBytes, 0L, 0L));
     }
 
     /**
@@ -611,7 +615,8 @@
      */
     public void recordHistory(NetworkStatsHistory input, long start, long end) {
         final NetworkStats.Entry entry = new NetworkStats.Entry(
-                IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
+                IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
         for (int i = 0; i < input.bucketCount; i++) {
             final long bucketStart = input.bucketStart[i];
             final long bucketEnd = bucketStart + input.bucketDuration;
@@ -854,7 +859,8 @@
         ensureBuckets(start, end);
 
         final NetworkStats.Entry entry = new NetworkStats.Entry(
-                IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
+                IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
         while (rxBytes > 1024 || rxPackets > 128 || txBytes > 1024 || txPackets > 128
                 || operations > 32) {
             final long curStart = randomLong(r, start, end);
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index 28f0699..1fbbd25 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -984,7 +984,16 @@
 
     /**
      * Firewall chain used for OEM-specific application restrictions.
-     * Denylist of apps that will not have network access due to OEM-specific restrictions.
+     *
+     * Denylist of apps that will not have network access due to OEM-specific restrictions. If an
+     * app UID is placed on this chain, and the chain is enabled, the app's packets will be dropped.
+     *
+     * All the {@code FIREWALL_CHAIN_OEM_DENY_x} chains are equivalent, and each one is
+     * independent of the others. The chains can be enabled and disabled independently, and apps can
+     * be added and removed from each chain independently.
+     *
+     * @see #FIREWALL_CHAIN_OEM_DENY_2
+     * @see #FIREWALL_CHAIN_OEM_DENY_3
      * @hide
      */
     @SystemApi(client = MODULE_LIBRARIES)
@@ -992,7 +1001,16 @@
 
     /**
      * Firewall chain used for OEM-specific application restrictions.
-     * Denylist of apps that will not have network access due to OEM-specific restrictions.
+     *
+     * Denylist of apps that will not have network access due to OEM-specific restrictions. If an
+     * app UID is placed on this chain, and the chain is enabled, the app's packets will be dropped.
+     *
+     * All the {@code FIREWALL_CHAIN_OEM_DENY_x} chains are equivalent, and each one is
+     * independent of the others. The chains can be enabled and disabled independently, and apps can
+     * be added and removed from each chain independently.
+     *
+     * @see #FIREWALL_CHAIN_OEM_DENY_1
+     * @see #FIREWALL_CHAIN_OEM_DENY_3
      * @hide
      */
     @SystemApi(client = MODULE_LIBRARIES)
@@ -1000,7 +1018,16 @@
 
     /**
      * Firewall chain used for OEM-specific application restrictions.
-     * Denylist of apps that will not have network access due to OEM-specific restrictions.
+     *
+     * Denylist of apps that will not have network access due to OEM-specific restrictions. If an
+     * app UID is placed on this chain, and the chain is enabled, the app's packets will be dropped.
+     *
+     * All the {@code FIREWALL_CHAIN_OEM_DENY_x} chains are equivalent, and each one is
+     * independent of the others. The chains can be enabled and disabled independently, and apps can
+     * be added and removed from each chain independently.
+     *
+     * @see #FIREWALL_CHAIN_OEM_DENY_1
+     * @see #FIREWALL_CHAIN_OEM_DENY_2
      * @hide
      */
     @SystemApi(client = MODULE_LIBRARIES)
@@ -1080,7 +1107,7 @@
     /**
      * Tests if a given integer represents a valid network type.
      * @param networkType the type to be tested
-     * @return a boolean.  {@code true} if the type is valid, else {@code false}
+     * @return {@code true} if the type is valid, else {@code false}
      * @deprecated All APIs accepting a network type are deprecated. There should be no need to
      *             validate a network type.
      */
@@ -1439,9 +1466,8 @@
     }
 
     /**
-     * Returns details about the currently active default data network
-     * for a given uid.  This is for internal use only to avoid spying
-     * other apps.
+     * Returns details about the currently active default data network for a given uid.
+     * This is for privileged use only to avoid spying on other apps.
      *
      * @return a {@link NetworkInfo} object for the current default network
      *        for the given uid or {@code null} if no default network is
@@ -1465,8 +1491,7 @@
     }
 
     /**
-     * Returns connection status information about a particular
-     * network type.
+     * Returns connection status information about a particular network type.
      *
      * @param networkType integer specifying which networkType in
      *        which you're interested.
@@ -1494,8 +1519,7 @@
     }
 
     /**
-     * Returns connection status information about a particular
-     * Network.
+     * Returns connection status information about a particular Network.
      *
      * @param network {@link Network} specifying which network
      *        in which you're interested.
@@ -1521,8 +1545,7 @@
     }
 
     /**
-     * Returns connection status information about all network
-     * types supported by the device.
+     * Returns connection status information about all network types supported by the device.
      *
      * @return an array of {@link NetworkInfo} objects.  Check each
      * {@link NetworkInfo#getType} for which type each applies.
@@ -1582,8 +1605,7 @@
     }
 
     /**
-     * Returns an array of all {@link Network} currently tracked by the
-     * framework.
+     * Returns an array of all {@link Network} currently tracked by the framework.
      *
      * @deprecated This method does not provide any notification of network state changes, forcing
      *             apps to call it repeatedly. This is inefficient and prone to race conditions.
@@ -1786,7 +1808,7 @@
      * that may be relevant for other components trying to detect captive portals.
      *
      * @hide
-     * @deprecated This API returns URL which is not guaranteed to be one of the URLs used by the
+     * @deprecated This API returns a URL which is not guaranteed to be one of the URLs used by the
      *             system.
      */
     @Deprecated
@@ -2365,8 +2387,7 @@
     }
 
     /**
-     * Request that keepalives be started on a TCP socket.
-     * The socket must be established.
+     * Request that keepalives be started on a TCP socket. The socket must be established.
      *
      * @param network The {@link Network} the socket is on.
      * @param socket The socket that needs to be kept alive.
@@ -2653,7 +2674,7 @@
     }
 
     /**
-     * Check if the package is a allowed to write settings. This also accounts that such an access
+     * Check if the package is allowed to write settings. This also records that such an access
      * happened.
      *
      * @return {@code true} iff the package is allowed to write settings.
@@ -2756,7 +2777,7 @@
     }
 
     /**
-     * Attempt to tether the named interface.  This will setup a dhcp server
+     * Attempt to tether the named interface.  This will set up a dhcp server
      * on the interface, forward and NAT IP packets and forward DNS requests
      * to the best active upstream network interface.  Note that if no upstream
      * IP network interface is available, dhcp will still run and traffic will be
@@ -3265,10 +3286,10 @@
 
     /**
      * Get the last value of the entitlement check on this downstream. If the cached value is
-     * {@link #TETHER_ERROR_NO_ERROR} or showEntitlementUi argument is false, it just return the
-     * cached value. Otherwise, a UI-based entitlement check would be performed. It is not
+     * {@link #TETHER_ERROR_NO_ERROR} or showEntitlementUi argument is false, this just returns the
+     * cached value. Otherwise, a UI-based entitlement check will be performed. It is not
      * guaranteed that the UI-based entitlement check will complete in any specific time period
-     * and may in fact never complete. Any successful entitlement check the platform performs for
+     * and it may in fact never complete. Any successful entitlement check the platform performs for
      * any reason will update the cached value.
      *
      * @param type the downstream type of tethering. Must be one of
@@ -3455,12 +3476,11 @@
     }
 
     /**
-     * Returns true if the hardware supports the given network type
-     * else it returns false.  This doesn't indicate we have coverage
-     * or are authorized onto a network, just whether or not the
-     * hardware supports it.  For example a GSM phone without a SIM
-     * should still return {@code true} for mobile data, but a wifi only
-     * tablet would return {@code false}.
+     * Returns whether the hardware supports the given network type.
+     *
+     * This doesn't indicate there is coverage or such a network is available, just whether the
+     * hardware supports it. For example a GSM phone without a SIM card will return {@code true}
+     * for mobile data, but a WiFi only tablet would return {@code false}.
      *
      * @param networkType The network type we'd like to check
      * @return {@code true} if supported, else {@code false}
@@ -4826,9 +4846,8 @@
      * Unregisters a {@code NetworkCallback} and possibly releases networks originating from
      * {@link #requestNetwork(NetworkRequest, NetworkCallback)} and
      * {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)} calls.
-     * If the given {@code NetworkCallback} had previously been used with
-     * {@code #requestNetwork}, any networks that had been connected to only to satisfy that request
-     * will be disconnected.
+     * If the given {@code NetworkCallback} had previously been used with {@code #requestNetwork},
+     * any networks that the device brought up only to satisfy that request will be disconnected.
      *
      * Notifications that would have triggered that {@code NetworkCallback} will immediately stop
      * triggering it as soon as this call returns.
@@ -4963,7 +4982,7 @@
     }
 
     /**
-     * Temporarily allow bad wifi to override {@code config_networkAvoidBadWifi} configuration.
+     * Temporarily allow bad Wi-Fi to override {@code config_networkAvoidBadWifi} configuration.
      *
      * @param timeMs The expired current time. The value should be set within a limited time from
      *               now.
@@ -5022,7 +5041,7 @@
     }
 
     /**
-     * Determine whether the device is configured to avoid bad wifi.
+     * Determine whether the device is configured to avoid bad Wi-Fi.
      * @hide
      */
     @SystemApi
@@ -5091,9 +5110,9 @@
      * each such operation.
      *
      * @param network The network on which the application desires to use multipath data.
-     *                If {@code null}, this method will return the a preference that will generally
+     *                If {@code null}, this method will return a preference that will generally
      *                apply to metered networks.
-     * @return a bitwise OR of zero or more of the  {@code MULTIPATH_PREFERENCE_*} constants.
+     * @return a bitwise OR of zero or more of the {@code MULTIPATH_PREFERENCE_*} constants.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
     public @MultipathPreference int getMultipathPreference(@Nullable Network network) {
@@ -5206,7 +5225,7 @@
      */
     @Nullable
     public Network getBoundNetworkForProcess() {
-        // Forcing callers to call thru non-static function ensures ConnectivityManager
+        // Forcing callers to call through non-static function ensures ConnectivityManager has been
         // instantiated.
         return getProcessDefaultNetwork();
     }
@@ -5851,7 +5870,7 @@
     }
 
     /**
-     * Removes the specified UID from the list of UIds that can use use background data on metered
+     * Removes the specified UID from the list of UIDs that can use background data on metered
      * networks if background data is not restricted. The deny list takes precedence over the
      * allow list.
      *
diff --git a/netd/BpfHandler.cpp b/netd/BpfHandler.cpp
index fad6bbb..2810d80 100644
--- a/netd/BpfHandler.cpp
+++ b/netd/BpfHandler.cpp
@@ -64,6 +64,16 @@
     return netdutils::status::ok;
 }
 
+static Status checkProgramAccessible(const char* programPath) {
+    unique_fd prog(retrieveProgram(programPath));
+    if (prog == -1) {
+        int ret = errno;
+        ALOGE("Failed to get program from %s: %s", programPath, strerror(ret));
+        return statusFromErrno(ret, "program retrieve failed");
+    }
+    return netdutils::status::ok;
+}
+
 static Status initPrograms(const char* cg2_path) {
     unique_fd cg_fd(open(cg2_path, O_DIRECTORY | O_RDONLY | O_CLOEXEC));
     if (cg_fd == -1) {
@@ -71,6 +81,10 @@
         ALOGE("Failed to open the cgroup directory: %s", strerror(ret));
         return statusFromErrno(ret, "Open the cgroup directory failed");
     }
+    RETURN_IF_NOT_OK(checkProgramAccessible(XT_BPF_ALLOWLIST_PROG_PATH));
+    RETURN_IF_NOT_OK(checkProgramAccessible(XT_BPF_DENYLIST_PROG_PATH));
+    RETURN_IF_NOT_OK(checkProgramAccessible(XT_BPF_EGRESS_PROG_PATH));
+    RETURN_IF_NOT_OK(checkProgramAccessible(XT_BPF_INGRESS_PROG_PATH));
     RETURN_IF_NOT_OK(attachProgramToCgroup(BPF_EGRESS_PROG_PATH, cg_fd, BPF_CGROUP_INET_EGRESS));
     RETURN_IF_NOT_OK(attachProgramToCgroup(BPF_INGRESS_PROG_PATH, cg_fd, BPF_CGROUP_INET_INGRESS));
     RETURN_IF_NOT_OK(attachProgramToCgroup(CGROUP_SOCKET_PROG_PATH, cg_fd, BPF_CGROUP_INET_SOCK_CREATE));
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index a95aa85..96c615b 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -27,12 +27,15 @@
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
 import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.IFACE_VT;
 import static android.net.NetworkStats.INTERFACES_ALL;
 import static android.net.NetworkStats.METERED_ALL;
+import static android.net.NetworkStats.METERED_NO;
 import static android.net.NetworkStats.METERED_YES;
 import static android.net.NetworkStats.ROAMING_ALL;
+import static android.net.NetworkStats.ROAMING_NO;
 import static android.net.NetworkStats.SET_ALL;
 import static android.net.NetworkStats.SET_DEFAULT;
 import static android.net.NetworkStats.SET_FOREGROUND;
@@ -2922,7 +2925,8 @@
             for (TetherStatsParcel tetherStats : tetherStatsParcels) {
                 try {
                     stats.combineValues(new NetworkStats.Entry(tetherStats.iface, UID_TETHERING,
-                            SET_DEFAULT, TAG_NONE, tetherStats.rxBytes, tetherStats.rxPackets,
+                            SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
+                            tetherStats.rxBytes, tetherStats.rxPackets,
                             tetherStats.txBytes, tetherStats.txPackets, 0L));
                 } catch (ArrayIndexOutOfBoundsException e) {
                     throw new IllegalStateException("invalid tethering stats " + e);
diff --git a/service/ServiceConnectivityResources/res/values-es-rUS/strings.xml b/service/ServiceConnectivityResources/res/values-es-rUS/strings.xml
index fdca468..b24dee0 100644
--- a/service/ServiceConnectivityResources/res/values-es-rUS/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-es-rUS/strings.xml
@@ -22,7 +22,7 @@
     <string name="network_available_sign_in" msgid="2622520134876355561">"Acceder a la red"</string>
     <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
     <skip />
-    <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>no tiene acceso a Internet"</string>
+    <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> no tiene acceso a Internet"</string>
     <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Presiona para ver opciones"</string>
     <string name="mobile_no_internet" msgid="4087718456753201450">"La red móvil no tiene acceso a Internet"</string>
     <string name="other_networks_no_internet" msgid="5693932964749676542">"La red no tiene acceso a Internet"</string>
diff --git a/service/ServiceConnectivityResources/res/values-or/strings.xml b/service/ServiceConnectivityResources/res/values-or/strings.xml
index 8b85884..49a773a 100644
--- a/service/ServiceConnectivityResources/res/values-or/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-or/strings.xml
@@ -17,7 +17,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"ସିଷ୍ଟମର ସଂଯୋଗ ସମ୍ବନ୍ଧିତ ରିସୋର୍ସଗୁଡ଼ିକ"</string>
+    <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"ସିଷ୍ଟମ କନେକ୍ଟିଭିଟୀ ରିସୋର୍ସ"</string>
     <string name="wifi_available_sign_in" msgid="8041178343789805553">"ୱାଇ-ଫାଇ ନେଟୱର୍କରେ ସାଇନ୍‍-ଇନ୍‍ କରନ୍ତୁ"</string>
     <string name="network_available_sign_in" msgid="2622520134876355561">"ନେଟ୍‌ୱର୍କରେ ସାଇନ୍‍ ଇନ୍‍ କରନ୍ତୁ"</string>
     <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
diff --git a/service/jni/com_android_server_BpfNetMaps.cpp b/service/jni/com_android_server_BpfNetMaps.cpp
index 2780044..11ba235 100644
--- a/service/jni/com_android_server_BpfNetMaps.cpp
+++ b/service/jni/com_android_server_BpfNetMaps.cpp
@@ -26,6 +26,8 @@
 #include <nativehelper/ScopedPrimitiveArray.h>
 #include <netjniutils/netjniutils.h>
 #include <net/if.h>
+#include <private/android_filesystem_config.h>
+#include <unistd.h>
 #include <vector>
 
 
@@ -48,6 +50,12 @@
 static void native_init(JNIEnv* env, jclass clazz) {
   Status status = mTc.start();
   CHECK_LOG(status);
+  if (!isOk(status)) {
+    uid_t uid = getuid();
+    ALOGE("BpfNetMaps jni init failure as uid=%d", uid);
+    // TODO: Fix tests to not use this jni lib, so we can unconditionally abort()
+    if (uid == AID_SYSTEM || uid == AID_NETWORK_STACK) abort();
+  }
 }
 
 static jint native_addNaughtyApp(JNIEnv* env, jobject self, jint uid) {
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index 8671f6b..594223c 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -439,16 +439,21 @@
     public void setChildChain(final int childChain, final boolean enable) {
         throwIfPreT("setChildChain is not available on pre-T devices");
 
-        final long match = getMatchByFirewallChain(childChain);
-        try {
-            synchronized (sUidRulesConfigBpfMapLock) {
-                final U32 config = sConfigurationMap.getValue(UID_RULES_CONFIGURATION_KEY);
-                final long newConfig = enable ? (config.val | match) : (config.val & ~match);
-                sConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, new U32(newConfig));
+        if (sEnableJavaBpfMap) {
+            final long match = getMatchByFirewallChain(childChain);
+            try {
+                synchronized (sUidRulesConfigBpfMapLock) {
+                    final U32 config = sConfigurationMap.getValue(UID_RULES_CONFIGURATION_KEY);
+                    final long newConfig = enable ? (config.val | match) : (config.val & ~match);
+                    sConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, new U32(newConfig));
+                }
+            } catch (ErrnoException e) {
+                throw new ServiceSpecificException(e.errno,
+                        "Unable to set child chain: " + Os.strerror(e.errno));
             }
-        } catch (ErrnoException e) {
-            throw new ServiceSpecificException(e.errno,
-                    "Unable to set child chain: " + Os.strerror(e.errno));
+        } else {
+            final int err = native_setChildChain(childChain, enable);
+            maybeThrow(err, "Unable to set child chain");
         }
     }
 
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 7b5026d..960059b 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -3604,10 +3604,9 @@
 
             switch (msg.what) {
                 case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
-                    nai.declaredCapabilitiesUnsanitized =
-                            new NetworkCapabilities((NetworkCapabilities) arg.second);
-                    final NetworkCapabilities sanitized = sanitizedCapabilitiesFromAgent(
-                            mCarrierPrivilegeAuthenticator, nai);
+                    nai.setDeclaredCapabilities((NetworkCapabilities) arg.second);
+                    final NetworkCapabilities sanitized =
+                            nai.getDeclaredCapabilitiesSanitized(mCarrierPrivilegeAuthenticator);
                     maybeUpdateWifiRoamTimestamp(nai, sanitized);
                     updateCapabilities(nai.getCurrentScore(), nai, sanitized);
                     break;
@@ -7328,11 +7327,11 @@
         // while the network monitor is starting.
         final LinkProperties lp = new LinkProperties(nai.linkProperties);
         // Store a copy of the declared capabilities.
-        nai.declaredCapabilitiesUnsanitized = new NetworkCapabilities(nai.networkCapabilities);
+        nai.setDeclaredCapabilities(nai.networkCapabilities);
         // Make sure the LinkProperties and NetworkCapabilities reflect what the agent info said.
-        final NetworkCapabilities nc =
-                sanitizedCapabilitiesFromAgent(mCarrierPrivilegeAuthenticator, nai);
-        nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc));
+        final NetworkCapabilities sanitized =
+                nai.getDeclaredCapabilitiesSanitized(mCarrierPrivilegeAuthenticator);
+        nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, sanitized));
         processLinkPropertiesFromAgent(nai, lp);
         nai.linkProperties = lp;
 
@@ -7795,38 +7794,6 @@
         }
     }
 
-    /**
-     * Sanitize capabilities coming from a network agent.
-     *
-     * Agents have restrictions on what capabilities they can send to Connectivity. For example,
-     * they can't change the owner UID from what they declared before, and complex restrictions
-     * apply to the accessUids field.
-     * They also should not mutate immutable capabilities, although for backward-compatibility
-     * this is not enforced and limited to just a log.
-     *
-     * This method returns a sanitized copy of the passed capabilities to make sure they don't
-     * contain stuff they should not, and should generally be called by code that accesses
-     * {@link NetworkAgentInfo#declaredCapabilitiesUnsanitized}.
-     */
-    // TODO : move this to NetworkAgentInfo
-    private NetworkCapabilities sanitizedCapabilitiesFromAgent(
-            final CarrierPrivilegeAuthenticator carrierPrivilegeAuthenticator,
-            @NonNull final NetworkAgentInfo nai) {
-        final NetworkCapabilities nc = new NetworkCapabilities(nai.declaredCapabilitiesUnsanitized);
-        if (nc.hasConnectivityManagedCapability()) {
-            Log.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
-        }
-        if (nai.networkCapabilities.getOwnerUid() != nc.getOwnerUid()) {
-            Log.e(TAG, nai.toShortString() + ": ignoring attempt to change owner from "
-                    + nai.networkCapabilities.getOwnerUid() + " to " + nc.getOwnerUid());
-            nc.setOwnerUid(nai.networkCapabilities.getOwnerUid());
-        }
-        NetworkAgentInfo.restrictCapabilitiesFromNetworkAgent(nc, nai.creatorUid,
-                mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE),
-                carrierPrivilegeAuthenticator);
-        return nc;
-    }
-
     /** Modifies |newNc| based on the capabilities of |underlyingNetworks| and |agentCaps|. */
     @VisibleForTesting
     void applyUnderlyingCapabilities(@Nullable Network[] underlyingNetworks,
@@ -7952,7 +7919,7 @@
 
         if (nai.propagateUnderlyingCapabilities()) {
             applyUnderlyingCapabilities(nai.declaredUnderlyingNetworks,
-                    sanitizedCapabilitiesFromAgent(mCarrierPrivilegeAuthenticator, nai),
+                    nai.getDeclaredCapabilitiesSanitized(mCarrierPrivilegeAuthenticator),
                     newNc);
         }
 
diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
index 04031af..c863165 100644
--- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
@@ -26,6 +26,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.net.CaptivePortalData;
 import android.net.DscpPolicy;
 import android.net.IDnsResolver;
@@ -184,9 +185,8 @@
     //
     // As the name implies, these capabilities are not sanitized and are not to
     // be trusted. Most callers should simply use the {@link networkCapabilities}
-    // field instead, and callers who need the declared capabilities should generally
-    // pass these to {@link ConnectivityService#sanitizedCapabilitiesFromAgent} before using them.
-    public @Nullable NetworkCapabilities declaredCapabilitiesUnsanitized;
+    // field instead.
+    private @Nullable NetworkCapabilities mDeclaredCapabilitiesUnsanitized;
 
     // Indicates if netd has been told to create this Network. From this point on the appropriate
     // routing rules are setup and routes are added so packets can begin flowing over the Network.
@@ -240,6 +240,53 @@
     // URL, Terms & Conditions URL, and network friendly name.
     public CaptivePortalData networkAgentPortalData;
 
+    /**
+     * Sets the capabilities sent by the agent for later retrieval.
+     *
+     * This method does not sanitize the capabilities ; instead, use
+     * {@link #getDeclaredCapabilitiesSanitized} to retrieve a sanitized
+     * copy of the capabilities as they were passed here.
+     *
+     * This method makes a defensive copy to avoid issues where the passed object is later mutated.
+     *
+     * @param caps the caps sent by the agent
+     */
+    public void setDeclaredCapabilities(@NonNull final NetworkCapabilities caps) {
+        mDeclaredCapabilitiesUnsanitized = new NetworkCapabilities(caps);
+    }
+
+    /**
+     * Get the latest capabilities sent by the network agent, after sanitizing them.
+     *
+     * These are the capabilities as they were sent by the agent (but sanitized to conform to
+     * their restrictions). They are NOT the capabilities currently applying to this agent ;
+     * for that, use {@link #networkCapabilities}.
+     *
+     * Agents have restrictions on what capabilities they can send to Connectivity. For example,
+     * they can't change the owner UID from what they declared before, and complex restrictions
+     * apply to the allowedUids field.
+     * They also should not mutate immutable capabilities, although for backward-compatibility
+     * this is not enforced and limited to just a log.
+     *
+     * @param carrierPrivilegeAuthenticator the authenticator, to check access UIDs.
+     */
+    public NetworkCapabilities getDeclaredCapabilitiesSanitized(
+            final CarrierPrivilegeAuthenticator carrierPrivilegeAuthenticator) {
+        final NetworkCapabilities nc = new NetworkCapabilities(mDeclaredCapabilitiesUnsanitized);
+        if (nc.hasConnectivityManagedCapability()) {
+            Log.wtf(TAG, "BUG: " + this + " has CS-managed capability.");
+        }
+        if (networkCapabilities.getOwnerUid() != nc.getOwnerUid()) {
+            Log.e(TAG, toShortString() + ": ignoring attempt to change owner from "
+                    + networkCapabilities.getOwnerUid() + " to " + nc.getOwnerUid());
+            nc.setOwnerUid(networkCapabilities.getOwnerUid());
+        }
+        restrictCapabilitiesFromNetworkAgent(nc, creatorUid,
+                mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE),
+                carrierPrivilegeAuthenticator);
+        return nc;
+    }
+
     // Networks are lingered when they become unneeded as a result of their NetworkRequests being
     // satisfied by a higher-scoring network. so as to allow communication to wrap up before the
     // network is taken down.  This usually only happens to the default network. Lingering ends with
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 5edf727..6ff2458 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -413,14 +413,17 @@
 
         // All tests in this class require a working Internet connection as they start. Make
         // sure there is still one as they end that's ready to use for the next test to use.
-        final TestNetworkCallback callback = new TestNetworkCallback();
-        registerDefaultNetworkCallback(callback);
-        try {
-            assertNotNull("Couldn't restore Internet connectivity", callback.waitForAvailable());
-        } finally {
-            // Unregister all registered callbacks.
-            unregisterRegisteredCallbacks();
-        }
+        mTestValidationConfigRule.runAfterNextCleanup(() -> {
+            final TestNetworkCallback callback = new TestNetworkCallback();
+            registerDefaultNetworkCallback(callback);
+            try {
+                assertNotNull("Couldn't restore Internet connectivity",
+                        callback.waitForAvailable());
+            } finally {
+                // Unregister all registered callbacks.
+                unregisterRegisteredCallbacks();
+            }
+        });
     }
 
     @Test
diff --git a/tests/cts/net/src/android/net/cts/DeviceConfigRule.kt b/tests/cts/net/src/android/net/cts/DeviceConfigRule.kt
index d31a4e0..3a739f2 100644
--- a/tests/cts/net/src/android/net/cts/DeviceConfigRule.kt
+++ b/tests/cts/net/src/android/net/cts/DeviceConfigRule.kt
@@ -21,6 +21,7 @@
 import android.provider.DeviceConfig
 import android.util.Log
 import com.android.modules.utils.build.SdkLevel
+import com.android.testutils.ExceptionUtils.ThrowingRunnable
 import com.android.testutils.runAsShell
 import com.android.testutils.tryTest
 import org.junit.rules.TestRule
@@ -51,7 +52,7 @@
     /**
      * Actions to be run after cleanup of the config, for the current test only.
      */
-    private val currentTestCleanupActions = mutableListOf<Runnable>()
+    private val currentTestCleanupActions = mutableListOf<ThrowingRunnable>()
 
     override fun apply(base: Statement, description: Description): Statement {
         return TestValidationUrlStatement(base, description)
@@ -93,8 +94,13 @@
                     originalConfig.clear()
                     usedConfig.clear()
                 } cleanup {
-                    currentTestCleanupActions.forEach { it.run() }
-                    currentTestCleanupActions.clear()
+                    // Fold all cleanup actions into cleanup steps of an empty tryTest, so they are
+                    // all run even if exceptions are thrown, and exceptions are reported properly.
+                    currentTestCleanupActions.fold(tryTest { }) {
+                        tryBlock, action -> tryBlock.cleanupStep { action.run() }
+                    }.cleanup {
+                        currentTestCleanupActions.clear()
+                    }
                 }
             }
         }
@@ -118,7 +124,7 @@
     /**
      * Add an action to be run after config cleanup when the current test case ends.
      */
-    fun runAfterNextCleanup(action: Runnable) {
+    fun runAfterNextCleanup(action: ThrowingRunnable) {
         currentTestCleanupActions.add(action)
     }
 }
diff --git a/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt b/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
index 77dca95..be2911b 100644
--- a/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
+++ b/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
@@ -78,6 +78,7 @@
 import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkCreated
 import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnDscpPolicyStatusUpdated
 import com.android.testutils.TestableNetworkCallback
+import com.android.net.module.util.IpUtils
 import org.junit.After
 import org.junit.Assume.assumeTrue
 import org.junit.Before
@@ -89,6 +90,7 @@
 import java.net.InetSocketAddress
 import java.nio.ByteBuffer
 import java.nio.ByteOrder
+import java.util.Arrays
 import java.util.regex.Pattern
 import kotlin.test.assertEquals
 import kotlin.test.assertNotNull
@@ -225,15 +227,13 @@
 
         var inet6Addr: Inet6Address? = null
         val timeout = SystemClock.elapsedRealtime() + PACKET_TIMEOUT_MS
+        val onLinkPrefix = raResponder.prefix
         while (timeout > SystemClock.elapsedRealtime()) {
             try {
                 // Pick any arbitrary port
                 Os.connect(sock, TEST_TARGET_IPV6_ADDR, 12345)
                 val sockAddr = Os.getsockname(sock) as InetSocketAddress
-
-                // TODO: make RouterAdvertisementResponder.SLAAC_PREFIX public and use it here,
-                // or make it configurable and configure it here.
-                if (IpPrefix("2001:db8::/64").contains(sockAddr.address)) {
+                if (onLinkPrefix.contains(sockAddr.address)) {
                     inet6Addr = sockAddr.address as Inet6Address
                     break
                 }
@@ -317,6 +317,9 @@
     }
 
     fun parseV4PacketDscp(buffer: ByteBuffer): Int {
+        // Validate checksum before parsing packet.
+        val calCheck = IpUtils.ipChecksum(buffer, Struct.getSize(EthernetHeader::class.java))
+
         val ip_ver = buffer.get()
         val tos = buffer.get()
         val length = buffer.getShort()
@@ -325,6 +328,8 @@
         val ttl = buffer.get()
         val ipType = buffer.get()
         val checksum = buffer.getShort()
+
+        assertEquals(0, calCheck, "Invalid IPv4 header checksum")
         return tos.toInt().shr(2)
     }
 
@@ -399,6 +404,7 @@
         val packets = generateSequence { reader.poll(PACKET_TIMEOUT_MS) }
         for (packet in packets) {
             val buffer = ByteBuffer.wrap(packet, 0, packet.size).order(ByteOrder.BIG_ENDIAN)
+
             // TODO: consider using Struct.parse for all packet parsing.
             val etherHdr = Struct.parse(EthernetHeader::class.java, buffer)
             val expectedType = if (sendV6) ETHER_TYPE_IPV6 else ETHER_TYPE_IPV4
@@ -442,6 +448,9 @@
             assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
         }
         validatePacket(agent, dscpValue = 1, dstPort = 4444)
+        // Send a second packet to validate that the stored BPF policy
+        // is correct for subsequent packets.
+        validatePacket(agent, dscpValue = 1, dstPort = 4444)
 
         agent.sendRemoveDscpPolicy(1)
         agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
@@ -480,6 +489,9 @@
             assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
         }
         validatePacket(agent, true, dscpValue = 1, dstPort = 4444)
+        // Send a second packet to validate that the stored BPF policy
+        // is correct for subsequent packets.
+        validatePacket(agent, true, dscpValue = 1, dstPort = 4444)
 
         agent.sendRemoveDscpPolicy(1)
         agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
index 89b107e..650450f 100644
--- a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
@@ -68,6 +68,7 @@
 import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
 import com.android.testutils.RecorderCallback.CallbackEntry.Lost
 import com.android.testutils.RouterAdvertisementResponder
+import com.android.testutils.SkipPresubmit
 import com.android.testutils.TapPacketReader
 import com.android.testutils.TestableNetworkCallback
 import com.android.testutils.anyNetwork
@@ -117,6 +118,7 @@
 @RunWith(DevSdkIgnoreRunner::class)
 // This test depends on behavior introduced post-T as part of connectivity module updates
 @ConnectivityModuleTest
+@SkipPresubmit(reason = "Flaky: b/240323229; remove annotation after fixing")
 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 class EthernetManagerTest {
 
diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
index 7254319..f035f72 100644
--- a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
@@ -16,11 +16,13 @@
 
 package android.net.cts.util;
 
+import static android.Manifest.permission.NETWORK_SETTINGS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_TEST;
 
 import static com.android.compatibility.common.util.PropertyUtil.getFirstApiLevel;
+import static com.android.testutils.TestPermissionUtil.runAsShell;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -288,7 +290,8 @@
         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
         mContext.registerReceiver(receiver, filter);
 
-        final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+        final WifiInfo wifiInfo = runAsShell(NETWORK_SETTINGS,
+                () -> mWifiManager.getConnectionInfo());
         final boolean wasWifiConnected = wifiInfo != null && wifiInfo.getNetworkId() != -1;
         // Assert that we can establish a TCP connection on wifi.
         Socket wifiBoundSocket = null;
diff --git a/tests/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/integration/util/com/android/server/NetworkAgentWrapper.java
index 2763f5a..97688d5 100644
--- a/tests/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -61,6 +61,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
 
 public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
     private final NetworkCapabilities mNetworkCapabilities;
@@ -83,14 +84,35 @@
     private final ArrayTrackRecord<CallbackType>.ReadHead mCallbackHistory =
             new ArrayTrackRecord<CallbackType>().newReadHead();
 
+    public static class Callbacks {
+        public final Consumer<NetworkAgent> onNetworkCreated;
+        public final Consumer<NetworkAgent> onNetworkUnwanted;
+        public final Consumer<NetworkAgent> onNetworkDestroyed;
+
+        public Callbacks() {
+            this(null, null, null);
+        }
+
+        public Callbacks(Consumer<NetworkAgent> onNetworkCreated,
+                Consumer<NetworkAgent> onNetworkUnwanted,
+                Consumer<NetworkAgent> onNetworkDestroyed) {
+            this.onNetworkCreated = onNetworkCreated;
+            this.onNetworkUnwanted = onNetworkUnwanted;
+            this.onNetworkDestroyed = onNetworkDestroyed;
+        }
+    }
+
+    private final Callbacks mCallbacks;
+
     public NetworkAgentWrapper(int transport, LinkProperties linkProperties,
             NetworkCapabilities ncTemplate, Context context) throws Exception {
-        this(transport, linkProperties, ncTemplate, null /* provider */, context);
+        this(transport, linkProperties, ncTemplate, null /* provider */,
+                null /* callbacks */, context);
     }
 
     public NetworkAgentWrapper(int transport, LinkProperties linkProperties,
             NetworkCapabilities ncTemplate, NetworkProvider provider,
-            Context context) throws Exception {
+            Callbacks callbacks, Context context) throws Exception {
         final int type = transportToLegacyType(transport);
         final String typeName = ConnectivityManager.getNetworkTypeName(type);
         mNetworkCapabilities = (ncTemplate != null) ? ncTemplate : new NetworkCapabilities();
@@ -135,6 +157,7 @@
                 .setLegacyTypeName(typeName)
                 .setLegacyExtraInfo(extraInfo)
                 .build();
+        mCallbacks = (callbacks != null) ? callbacks : new Callbacks();
         mNetworkAgent = makeNetworkAgent(linkProperties, mNetworkAgentConfig, provider);
     }
 
@@ -214,6 +237,31 @@
         protected void removeKeepalivePacketFilter(Message msg) {
             Log.i(mWrapper.mLogTag, "Remove keepalive packet filter.");
         }
+
+        @Override
+        public void onNetworkCreated() {
+            super.onNetworkCreated();
+            if (mWrapper.mCallbacks.onNetworkCreated != null) {
+                mWrapper.mCallbacks.onNetworkCreated.accept(this);
+            }
+        }
+
+        @Override
+        public void onNetworkUnwanted() {
+            super.onNetworkUnwanted();
+            if (mWrapper.mCallbacks.onNetworkUnwanted != null) {
+                mWrapper.mCallbacks.onNetworkUnwanted.accept(this);
+            }
+        }
+
+        @Override
+        public void onNetworkDestroyed() {
+            super.onNetworkDestroyed();
+            if (mWrapper.mCallbacks.onNetworkDestroyed != null) {
+                mWrapper.mCallbacks.onNetworkDestroyed.accept(this);
+            }
+        }
+
     }
 
     public void setScore(@NonNull final NetworkScore score) {
diff --git a/tests/native/utilities/Android.bp b/tests/native/utilities/Android.bp
index 7668cc1..4706b3d 100644
--- a/tests/native/utilities/Android.bp
+++ b/tests/native/utilities/Android.bp
@@ -13,6 +13,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_test_library {
     name: "libconnectivity_native_test_utils",
     defaults: [
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index 0908ad2..72324d9 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -150,6 +150,5 @@
     jni_libs: [
         "libandroid_net_connectivity_com_android_net_module_util_jni",
         "libservice-connectivity",
-        "libandroid_net_connectivity_com_android_net_module_util_jni",
     ],
 }
diff --git a/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java b/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
index 71c03ff..8a537be 100644
--- a/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
+++ b/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
@@ -88,20 +88,28 @@
 
         Entry uid1Entry1 = new Entry("if1", uid1,
                 android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
+                android.net.NetworkStats.METERED_NO, android.net.NetworkStats.ROAMING_NO,
+                android.net.NetworkStats.DEFAULT_NETWORK_NO,
                 100, 10, 200, 20, 0);
 
         Entry uid1Entry2 = new Entry(
                 "if2", uid1,
                 android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
+                android.net.NetworkStats.METERED_NO, android.net.NetworkStats.ROAMING_NO,
+                android.net.NetworkStats.DEFAULT_NETWORK_NO,
                 100, 10, 200, 20, 0);
 
         Entry uid2Entry1 = new Entry("if1", uid2,
                 android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
+                android.net.NetworkStats.METERED_NO, android.net.NetworkStats.ROAMING_NO,
+                android.net.NetworkStats.DEFAULT_NETWORK_NO,
                 150, 10, 250, 20, 0);
 
         Entry uid2Entry2 = new Entry(
                 "if2", uid2,
                 android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
+                android.net.NetworkStats.METERED_NO, android.net.NetworkStats.ROAMING_NO,
+                android.net.NetworkStats.DEFAULT_NETWORK_NO,
                 150, 10, 250, 20, 0);
 
         NetworkStatsHistory history1 = new NetworkStatsHistory(10, 2);
diff --git a/tests/unit/java/android/net/NetworkStatsCollectionTest.java b/tests/unit/java/android/net/NetworkStatsCollectionTest.java
index b518a61..a6e9e95 100644
--- a/tests/unit/java/android/net/NetworkStatsCollectionTest.java
+++ b/tests/unit/java/android/net/NetworkStatsCollectionTest.java
@@ -18,6 +18,10 @@
 
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.NetworkIdentity.OEM_NONE;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.IFACE_ALL;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
 import static android.net.NetworkStats.SET_ALL;
 import static android.net.NetworkStats.SET_DEFAULT;
 import static android.net.NetworkStats.TAG_NONE;
@@ -480,7 +484,8 @@
         ident.add(new NetworkIdentity(ConnectivityManager.TYPE_MOBILE, -1, TEST_IMSI, null,
                 false, true, true, OEM_NONE, TEST_SUBID));
         large.recordData(ident, UID_ALL, SET_ALL, TAG_NONE, TIME_A, TIME_B,
-                new NetworkStats.Entry(12_730_893_164L, 1, 0, 0, 0));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 12_730_893_164L, 1, 0, 0, 0));
 
         // Verify untouched total
         assertEquals(12_730_893_164L, getHistory(large, null, TIME_A, TIME_C).getTotalBytes());
@@ -659,26 +664,33 @@
 
     private static void assertEntry(long rxBytes, long rxPackets, long txBytes, long txPackets,
             NetworkStats.Entry actual) {
-        assertEntry(new NetworkStats.Entry(rxBytes, rxPackets, txBytes, txPackets, 0L), actual);
+        assertEntry(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes, rxPackets, txBytes, txPackets, 0L),
+                actual);
     }
 
     private static void assertEntry(long rxBytes, long rxPackets, long txBytes, long txPackets,
             NetworkStatsHistory.Entry actual) {
-        assertEntry(new NetworkStats.Entry(rxBytes, rxPackets, txBytes, txPackets, 0L), actual);
+        assertEntry(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes, rxPackets, txBytes, txPackets, 0L),
+                actual);
     }
 
     private static void assertEntry(NetworkStats.Entry expected,
             NetworkStatsHistory.Entry actual) {
-        assertEntry(expected, new NetworkStats.Entry(actual.rxBytes, actual.rxPackets,
+        assertEntry(expected, new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE,
+                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, actual.rxBytes, actual.rxPackets,
                 actual.txBytes, actual.txPackets, 0L));
     }
 
     private static void assertEntry(NetworkStatsHistory.Entry expected,
             NetworkStatsHistory.Entry actual) {
-        assertEntry(new NetworkStats.Entry(actual.rxBytes, actual.rxPackets,
-                actual.txBytes, actual.txPackets, 0L),
-                new NetworkStats.Entry(actual.rxBytes, actual.rxPackets,
-                actual.txBytes, actual.txPackets, 0L));
+        assertEntry(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                        ROAMING_NO, DEFAULT_NETWORK_NO, actual.rxBytes, actual.rxPackets,
+                       actual.txBytes, actual.txPackets, 0L),
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                        ROAMING_NO, DEFAULT_NETWORK_NO, actual.rxBytes, actual.rxPackets,
+                        actual.txBytes, actual.txPackets, 0L));
     }
 
     private static void assertEntry(NetworkStats.Entry expected,
diff --git a/tests/unit/java/android/net/NetworkStatsHistoryTest.java b/tests/unit/java/android/net/NetworkStatsHistoryTest.java
index 43e331b..2170882 100644
--- a/tests/unit/java/android/net/NetworkStatsHistoryTest.java
+++ b/tests/unit/java/android/net/NetworkStatsHistoryTest.java
@@ -16,6 +16,13 @@
 
 package android.net;
 
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.IFACE_ALL;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
+import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.TAG_NONE;
+import static android.net.NetworkStats.UID_ALL;
 import static android.net.NetworkStatsHistory.DataStreamUtils.readVarLong;
 import static android.net.NetworkStatsHistory.DataStreamUtils.writeVarLong;
 import static android.net.NetworkStatsHistory.Entry.UNKNOWN;
@@ -110,7 +117,8 @@
 
         // record data into narrow window to get single bucket
         stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS,
-                new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 2L));
 
         assertEquals(1, stats.size());
         assertValues(stats, 0, SECOND_IN_MILLIS, 1024L, 10L, 2048L, 20L, 2L);
@@ -124,7 +132,8 @@
         // split equally across two buckets
         final long recordStart = TEST_START + (bucketDuration / 2);
         stats.recordData(recordStart, recordStart + bucketDuration,
-                new NetworkStats.Entry(1024L, 10L, 128L, 2L, 2L));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 128L, 2L, 2L));
 
         assertEquals(2, stats.size());
         assertValues(stats, 0, HOUR_IN_MILLIS / 2, 512L, 5L, 64L, 1L, 1L);
@@ -141,7 +150,8 @@
         final long recordStart = (TEST_START + BUCKET_SIZE) - MINUTE_IN_MILLIS;
         final long recordEnd = (TEST_START + (BUCKET_SIZE * 2)) + (MINUTE_IN_MILLIS * 4);
         stats.recordData(recordStart, recordEnd,
-                new NetworkStats.Entry(1000L, 2000L, 5000L, 10000L, 100L));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1000L, 2000L, 5000L, 10000L, 100L));
 
         assertEquals(3, stats.size());
         // first bucket should have (1/20 of value)
@@ -161,9 +171,11 @@
         final long firstStart = TEST_START;
         final long lastStart = TEST_START + WEEK_IN_MILLIS;
         stats.recordData(firstStart, firstStart + SECOND_IN_MILLIS,
-                new NetworkStats.Entry(128L, 2L, 256L, 4L, 1L));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 128L, 2L, 256L, 4L, 1L));
         stats.recordData(lastStart, lastStart + SECOND_IN_MILLIS,
-                new NetworkStats.Entry(64L, 1L, 512L, 8L, 2L));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 64L, 1L, 512L, 8L, 2L));
 
         // we should have two buckets, far apart from each other
         assertEquals(2, stats.size());
@@ -174,7 +186,8 @@
         final long middleStart = TEST_START + DAY_IN_MILLIS;
         final long middleEnd = middleStart + (HOUR_IN_MILLIS * 2);
         stats.recordData(middleStart, middleEnd,
-                new NetworkStats.Entry(2048L, 4L, 2048L, 4L, 2L));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 2048L, 4L, 2048L, 4L, 2L));
 
         // now should have four buckets, with new record in middle two buckets
         assertEquals(4, stats.size());
@@ -191,10 +204,12 @@
 
         // record some data in one bucket, and another overlapping buckets
         stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS,
-                new NetworkStats.Entry(256L, 2L, 256L, 2L, 1L));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 256L, 2L, 256L, 2L, 1L));
         final long midStart = TEST_START + (HOUR_IN_MILLIS / 2);
         stats.recordData(midStart, midStart + HOUR_IN_MILLIS,
-                new NetworkStats.Entry(1024L, 10L, 1024L, 10L, 10L));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 1024L, 10L, 10L));
 
         // should have two buckets, with some data mixed together
         assertEquals(2, stats.size());
@@ -371,9 +386,11 @@
                 MINUTE_IN_MILLIS, 0, FIELD_RX_BYTES | FIELD_TX_BYTES);
 
         history.recordData(0, MINUTE_IN_MILLIS,
-                new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 4L));
         history.recordData(0, 2 * MINUTE_IN_MILLIS,
-                new NetworkStats.Entry(2L, 2L, 2L, 2L, 2L));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 2L, 2L, 2L, 2L, 2L));
 
         assertFullValues(history, UNKNOWN, 1026L, UNKNOWN, 2050L, UNKNOWN, UNKNOWN);
     }
@@ -385,7 +402,8 @@
                 MINUTE_IN_MILLIS, 0, FIELD_RX_PACKETS | FIELD_OPERATIONS);
 
         full.recordData(0, MINUTE_IN_MILLIS,
-                new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 4L));
         partial.recordEntireHistory(full);
 
         assertFullValues(partial, UNKNOWN, UNKNOWN, 10L, UNKNOWN, UNKNOWN, 4L);
@@ -398,7 +416,8 @@
                 MINUTE_IN_MILLIS, 0, FIELD_RX_PACKETS | FIELD_OPERATIONS);
 
         partial.recordData(0, MINUTE_IN_MILLIS,
-                new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 4L));
         full.recordEntireHistory(partial);
 
         assertFullValues(full, MINUTE_IN_MILLIS, 0L, 10L, 0L, 0L, 4L);
@@ -408,9 +427,11 @@
     public void testSerialize() throws Exception {
         final NetworkStatsHistory before = new NetworkStatsHistory(MINUTE_IN_MILLIS, 40, FIELD_ALL);
         before.recordData(0, 4 * MINUTE_IN_MILLIS,
-                new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 4L));
         before.recordData(DAY_IN_MILLIS, DAY_IN_MILLIS + MINUTE_IN_MILLIS,
-                new NetworkStats.Entry(10L, 20L, 30L, 40L, 50L));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 10L, 20L, 30L, 40L, 50L));
 
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
         before.writeToStream(new DataOutputStream(out));
@@ -451,11 +472,14 @@
         final long THIRD_END = THIRD_START + (2 * HOUR_IN_MILLIS);
 
         stats.recordData(FIRST_START, FIRST_END,
-                new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 2L));
         stats.recordData(SECOND_START, SECOND_END,
-                new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 2L));
         stats.recordData(THIRD_START, THIRD_END,
-                new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 2L));
 
         // should have buckets: 2+1+2
         assertEquals(5, stats.size());
@@ -494,11 +518,14 @@
         final long THIRD_END = THIRD_START + (2 * HOUR_IN_MILLIS);
 
         stats.recordData(FIRST_START, FIRST_END,
-                new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 2L));
         stats.recordData(SECOND_START, SECOND_END,
-                new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 2L));
         stats.recordData(THIRD_START, THIRD_END,
-                new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 2L));
 
         assertFalse(stats.intersects(10, 20));
         assertFalse(stats.intersects(TEST_START + YEAR_IN_MILLIS, TEST_START + YEAR_IN_MILLIS + 1));
@@ -520,7 +547,8 @@
     public void testSetValues() throws Exception {
         stats = new NetworkStatsHistory(HOUR_IN_MILLIS);
         stats.recordData(TEST_START, TEST_START + 1,
-                new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L));
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 2L));
 
         assertEquals(1024L + 2048L, stats.getTotalBytes());
 
diff --git a/tests/unit/java/android/net/NetworkStatsTest.java b/tests/unit/java/android/net/NetworkStatsTest.java
index 6d79869..709b722 100644
--- a/tests/unit/java/android/net/NetworkStatsTest.java
+++ b/tests/unit/java/android/net/NetworkStatsTest.java
@@ -960,7 +960,7 @@
 
         // Ipv4 traffic sent/received by an app on stacked interface.
         final NetworkStats.Entry appEntry = new NetworkStats.Entry(
-                v4Iface, appUid, SET_DEFAULT, TAG_NONE,
+                v4Iface, appUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
                 30501490  /* rxBytes */,
                 22401 /* rxPackets */,
                 876235 /* txBytes */,
@@ -969,7 +969,8 @@
 
         // Traffic measured for the root uid on the base interface.
         final NetworkStats.Entry rootUidEntry = new NetworkStats.Entry(
-                baseIface, rootUid, SET_DEFAULT, TAG_NONE,
+                baseIface, rootUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO,
                 163577 /* rxBytes */,
                 187 /* rxPackets */,
                 17607 /* txBytes */,
@@ -977,7 +978,8 @@
                 0 /* operations */);
 
         final NetworkStats.Entry otherEntry = new NetworkStats.Entry(
-                otherIface, appUid, SET_DEFAULT, TAG_NONE,
+                otherIface, appUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO,
                 2600  /* rxBytes */,
                 2 /* rxPackets */,
                 3800 /* txBytes */,
@@ -993,14 +995,14 @@
 
         assertEquals(3, stats.size());
         final NetworkStats.Entry expectedAppUid = new NetworkStats.Entry(
-                v4Iface, appUid, SET_DEFAULT, TAG_NONE,
+                v4Iface, appUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
                 30949510,
                 22401,
                 1152335,
                 13805,
                 0);
         final NetworkStats.Entry expectedRootUid = new NetworkStats.Entry(
-                baseIface, 0, SET_DEFAULT, TAG_NONE,
+                baseIface, 0, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
                 163577,
                 187,
                 17607,
@@ -1014,14 +1016,16 @@
     @Test
     public void testApply464xlatAdjustments_noStackedIface() {
         NetworkStats.Entry firstEntry = new NetworkStats.Entry(
-                "if1", 10002, SET_DEFAULT, TAG_NONE,
+                "if1", 10002, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO,
                 2600  /* rxBytes */,
                 2 /* rxPackets */,
                 3800 /* txBytes */,
                 3 /* txPackets */,
                 0 /* operations */);
         NetworkStats.Entry secondEntry = new NetworkStats.Entry(
-                "if2", 10002, SET_DEFAULT, TAG_NONE,
+                "if2", 10002, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO,
                 5000  /* rxBytes */,
                 3 /* rxPackets */,
                 6000 /* txBytes */,
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 28119d8..06f7300 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -241,6 +241,7 @@
 import android.net.ConnectivityThread;
 import android.net.DataStallReportParcelable;
 import android.net.EthernetManager;
+import android.net.EthernetNetworkSpecifier;
 import android.net.IConnectivityDiagnosticsCallback;
 import android.net.IDnsResolver;
 import android.net.INetd;
@@ -428,6 +429,7 @@
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 import java.util.regex.Matcher;
@@ -922,9 +924,6 @@
         private int mProbesSucceeded;
         private String mNmValidationRedirectUrl = null;
         private boolean mNmProvNotificationRequested = false;
-        private Runnable mCreatedCallback;
-        private Runnable mUnwantedCallback;
-        private Runnable mDisconnectedCallback;
 
         private final ConditionVariable mNetworkStatusReceived = new ConditionVariable();
         // Contains the redirectUrl from networkStatus(). Before reading, wait for
@@ -932,22 +931,34 @@
         private String mRedirectUrl;
 
         TestNetworkAgentWrapper(int transport) throws Exception {
-            this(transport, new LinkProperties(), null /* ncTemplate */, null /* provider */);
+            this(transport, new LinkProperties(), null /* ncTemplate */, null /* provider */, null);
         }
 
         TestNetworkAgentWrapper(int transport, LinkProperties linkProperties)
                 throws Exception {
-            this(transport, linkProperties, null /* ncTemplate */, null /* provider */);
+            this(transport, linkProperties, null /* ncTemplate */, null /* provider */, null);
         }
 
         private TestNetworkAgentWrapper(int transport, LinkProperties linkProperties,
                 NetworkCapabilities ncTemplate) throws Exception {
-            this(transport, linkProperties, ncTemplate, null /* provider */);
+            this(transport, linkProperties, ncTemplate, null /* provider */, null);
         }
 
         private TestNetworkAgentWrapper(int transport, LinkProperties linkProperties,
                 NetworkCapabilities ncTemplate, NetworkProvider provider) throws Exception {
-            super(transport, linkProperties, ncTemplate, provider, mServiceContext);
+            this(transport, linkProperties, ncTemplate, provider /* provider */, null);
+        }
+
+        private TestNetworkAgentWrapper(int transport, NetworkAgentWrapper.Callbacks callbacks)
+                throws Exception {
+            this(transport, new LinkProperties(), null /* ncTemplate */, null /* provider */,
+                    callbacks);
+        }
+
+        private TestNetworkAgentWrapper(int transport, LinkProperties linkProperties,
+                NetworkCapabilities ncTemplate, NetworkProvider provider,
+                NetworkAgentWrapper.Callbacks callbacks) throws Exception {
+            super(transport, linkProperties, ncTemplate, provider, callbacks, mServiceContext);
 
             // Waits for the NetworkAgent to be registered, which includes the creation of the
             // NetworkMonitor.
@@ -968,23 +979,6 @@
                 mNetworkStatusReceived.open();
             }
 
-            @Override
-            public void onNetworkCreated() {
-                super.onNetworkCreated();
-                if (mCreatedCallback != null) mCreatedCallback.run();
-            }
-
-            @Override
-            public void onNetworkUnwanted() {
-                super.onNetworkUnwanted();
-                if (mUnwantedCallback != null) mUnwantedCallback.run();
-            }
-
-            @Override
-            public void onNetworkDestroyed() {
-                super.onNetworkDestroyed();
-                if (mDisconnectedCallback != null) mDisconnectedCallback.run();
-            }
         }
 
         @Override
@@ -1214,18 +1208,6 @@
             p.timestampMillis = DATA_STALL_TIMESTAMP;
             mNmCallbacks.notifyDataStallSuspected(p);
         }
-
-        public void setCreatedCallback(Runnable r) {
-            mCreatedCallback = r;
-        }
-
-        public void setUnwantedCallback(Runnable r) {
-            mUnwantedCallback = r;
-        }
-
-        public void setDisconnectedCallback(Runnable r) {
-            mDisconnectedCallback = r;
-        }
     }
 
     /**
@@ -3566,37 +3548,35 @@
         final NetworkRequest request = new NetworkRequest.Builder()
                 .addTransportType(TRANSPORT_WIFI).build();
         final TestNetworkCallback callback = new TestNetworkCallback();
-        final AtomicReference<Network> wifiNetwork = new AtomicReference<>();
-        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
 
         // Expectations for state when various callbacks fire. These expectations run on the handler
         // thread and not on the test thread because they need to prevent the handler thread from
         // advancing while they examine state.
 
         // 1. When onCreated fires, netd has been told to create the network.
-        mWiFiNetworkAgent.setCreatedCallback(() -> {
+        final Consumer<NetworkAgent> onNetworkCreated = (agent) -> {
             eventOrder.offer("onNetworkCreated");
-            wifiNetwork.set(mWiFiNetworkAgent.getNetwork());
-            assertNotNull(wifiNetwork.get());
             try {
                 verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
-                        wifiNetwork.get().getNetId(), INetd.PERMISSION_NONE));
+                        agent.getNetwork().getNetId(), INetd.PERMISSION_NONE));
             } catch (RemoteException impossible) {
                 fail();
             }
-        });
+        };
 
         // 2. onNetworkUnwanted isn't precisely ordered with respect to any particular events. Just
         //    check that it is fired at some point after disconnect.
-        mWiFiNetworkAgent.setUnwantedCallback(() -> eventOrder.offer("onNetworkUnwanted"));
+        final Consumer<NetworkAgent> onNetworkUnwanted = (agent) -> {
+            eventOrder.offer("onNetworkUnwanted");
+        };
 
         // 3. While the teardown timer is running, connectivity APIs report the network is gone, but
         //    netd has not yet been told to destroy it.
-        final Runnable duringTeardown = () -> {
+        final Consumer<Network> duringTeardown = (network) -> {
             eventOrder.offer("timePasses");
-            assertNull(mCm.getLinkProperties(wifiNetwork.get()));
+            assertNull(mCm.getLinkProperties(network));
             try {
-                verify(mMockNetd, never()).networkDestroy(wifiNetwork.get().getNetId());
+                verify(mMockNetd, never()).networkDestroy(network.getNetId());
             } catch (RemoteException impossible) {
                 fail();
             }
@@ -3604,15 +3584,20 @@
 
         // 4. After onNetworkDisconnected is called, connectivity APIs report the network is gone,
         // and netd has been told to destroy it.
-        mWiFiNetworkAgent.setDisconnectedCallback(() -> {
+        final Consumer<NetworkAgent> onNetworkDisconnected = (agent) -> {
             eventOrder.offer("onNetworkDisconnected");
-            assertNull(mCm.getLinkProperties(wifiNetwork.get()));
+            assertNull(mCm.getLinkProperties(agent.getNetwork()));
             try {
-                verify(mMockNetd).networkDestroy(wifiNetwork.get().getNetId());
+                verify(mMockNetd).networkDestroy(agent.getNetwork().getNetId());
             } catch (RemoteException impossible) {
                 fail();
             }
-        });
+        };
+
+        final NetworkAgentWrapper.Callbacks callbacks = new NetworkAgentWrapper.Callbacks(
+                onNetworkCreated, onNetworkUnwanted, onNetworkDisconnected);
+
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, callbacks);
 
         // Connect a network, and file a request for it after it has come up, to ensure the nascent
         // timer is cleared and the test does not have to wait for it. Filing the request after the
@@ -3634,7 +3619,7 @@
         // down the network and started the teardown timer, and short enough that the lambda is
         // scheduled to run before the teardown timer.
         final Handler h = new Handler(mCsHandlerThread.getLooper());
-        h.postDelayed(duringTeardown, 150);
+        h.postDelayed(() -> duringTeardown.accept(mWiFiNetworkAgent.getNetwork()), 150);
 
         // Disconnect the network and check that events happened in the right order.
         mCm.unregisterNetworkCallback(callback);
@@ -8547,8 +8532,7 @@
 
         // Enable always-on VPN lockdown. The main user loses network access because no VPN is up.
         final ArrayList<String> allowList = new ArrayList<>();
-        mVpnManagerService.setAlwaysOnVpnPackage(PRIMARY_USER, ALWAYS_ON_PACKAGE,
-                true /* lockdown */, allowList);
+        mMockVpn.setAlwaysOnPackage(ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
         waitForIdle();
         assertNull(mCm.getActiveNetworkForUid(uid));
         // This is arguably overspecified: a UID that is not running doesn't have an active network.
@@ -8580,8 +8564,7 @@
         assertNull(mCm.getActiveNetworkForUid(uid));
         assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
 
-        mVpnManagerService.setAlwaysOnVpnPackage(PRIMARY_USER, null, false /* lockdown */,
-                allowList);
+        mMockVpn.setAlwaysOnPackage(null, false /* lockdown */, allowList);
         waitForIdle();
     }
 
@@ -9039,10 +9022,8 @@
                 new Handler(ConnectivityThread.getInstanceLooper()));
 
         final int uid = Process.myUid();
-        final int userId = UserHandle.getUserId(uid);
         final ArrayList<String> allowList = new ArrayList<>();
-        mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */,
-                allowList);
+        mMockVpn.setAlwaysOnPackage(ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
         waitForIdle();
 
         final Set<Integer> excludedUids = new ArraySet<Integer>();
@@ -9072,7 +9053,7 @@
         assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
 
         // Disable lockdown, expect to see the network unblocked.
-        mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+        mMockVpn.setAlwaysOnPackage(null, false /* lockdown */, allowList);
         callback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
         defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
         vpnUidCallback.assertNoCallback();
@@ -9087,8 +9068,7 @@
 
         // Add our UID to the allowlist and re-enable lockdown, expect network is not blocked.
         allowList.add(TEST_PACKAGE_NAME);
-        mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */,
-                allowList);
+        mMockVpn.setAlwaysOnPackage(ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
         callback.assertNoCallback();
         defaultCallback.assertNoCallback();
         vpnUidCallback.assertNoCallback();
@@ -9126,12 +9106,11 @@
 
         // Disable lockdown, remove our UID from the allowlist, and re-enable lockdown.
         // Everything should now be blocked.
-        mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+        mMockVpn.setAlwaysOnPackage(null, false /* lockdown */, allowList);
         waitForIdle();
         expectNetworkRejectNonSecureVpn(inOrder, false, uidRangeParcelsAlsoExcludingUs);
         allowList.clear();
-        mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */,
-                allowList);
+        mMockVpn.setAlwaysOnPackage(ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
         waitForIdle();
         expectNetworkRejectNonSecureVpn(inOrder, true, uidRangeParcels);
         defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent);
@@ -9146,7 +9125,7 @@
         assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
 
         // Disable lockdown. Everything is unblocked.
-        mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+        mMockVpn.setAlwaysOnPackage(null, false /* lockdown */, allowList);
         defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
         assertBlockedCallbackInAnyOrder(callback, false, mWiFiNetworkAgent, mCellNetworkAgent);
         vpnUidCallback.assertNoCallback();
@@ -9160,8 +9139,7 @@
 
         // Enable and disable an always-on VPN package without lockdown. Expect no changes.
         reset(mMockNetd);
-        mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, false /* lockdown */,
-                allowList);
+        mMockVpn.setAlwaysOnPackage(ALWAYS_ON_PACKAGE, false /* lockdown */, allowList);
         inOrder.verify(mMockNetd, never()).networkRejectNonSecureVpn(anyBoolean(), any());
         callback.assertNoCallback();
         defaultCallback.assertNoCallback();
@@ -9174,7 +9152,7 @@
         assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
 
-        mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+        mMockVpn.setAlwaysOnPackage(null, false /* lockdown */, allowList);
         inOrder.verify(mMockNetd, never()).networkRejectNonSecureVpn(anyBoolean(), any());
         callback.assertNoCallback();
         defaultCallback.assertNoCallback();
@@ -9188,8 +9166,7 @@
         assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
 
         // Enable lockdown and connect a VPN. The VPN is not blocked.
-        mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */,
-                allowList);
+        mMockVpn.setAlwaysOnPackage(ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
         defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent);
         assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent);
         vpnUidCallback.assertNoCallback();
@@ -15723,6 +15700,45 @@
         mCm.unregisterNetworkCallback(cb);
     }
 
+    @Test
+    public void testSanitizedCapabilitiesFromAgentDoesNotMutateArgument()
+            throws Exception {
+        // This NetworkCapabilities builds an usual object to maximize the chance that this requires
+        // sanitization, so we have a high chance to detect any changes to the original.
+        final NetworkCapabilities unsanitized = new NetworkCapabilities.Builder()
+                .withoutDefaultCapabilities()
+                .addTransportType(TRANSPORT_WIFI)
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .setOwnerUid(12345)
+                .setAdministratorUids(new int[] {12345, 23456, 34567})
+                .setLinkUpstreamBandwidthKbps(20)
+                .setLinkDownstreamBandwidthKbps(10)
+                .setNetworkSpecifier(new EthernetNetworkSpecifier("foobar"))
+                .setTransportInfo(new WifiInfo.Builder().setBssid("AA:AA:AA:AA:AA:AA").build())
+                .setSignalStrength(-75)
+                .setSsid("SSID1")
+                .setRequestorUid(98765)
+                .setRequestorPackageName("TestPackage")
+                .setSubscriptionIds(Collections.singleton(Process.myUid()))
+                .setUids(UidRange.toIntRanges(uidRangesForUids(
+                        UserHandle.getUid(PRIMARY_USER, 10100),
+                        UserHandle.getUid(SECONDARY_USER, 10101),
+                        UserHandle.getUid(TERTIARY_USER, 10043))))
+                .setAllowedUids(Set.of(45678, 56789, 65432))
+                .setUnderlyingNetworks(List.of(new Network(99999)))
+                .build();
+        final NetworkCapabilities copyOfUnsanitized = new NetworkCapabilities(unsanitized);
+        final NetworkInfo info = new NetworkInfo(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_LTE,
+                ConnectivityManager.getNetworkTypeName(TYPE_MOBILE),
+                TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_LTE));
+        final NetworkAgentInfo agent = fakeNai(unsanitized, info);
+        agent.setDeclaredCapabilities(unsanitized);
+        final NetworkCapabilities sanitized = agent.getDeclaredCapabilitiesSanitized(
+                null /* carrierPrivilegeAuthenticator */);
+        assertEquals(copyOfUnsanitized, unsanitized);
+        assertNotEquals(sanitized, unsanitized);
+    }
+
     /**
      * Validate request counts are counted accurately on setProfileNetworkPreference on set/replace.
      */
diff --git a/tests/unit/java/com/android/server/VpnManagerServiceTest.java b/tests/unit/java/com/android/server/VpnManagerServiceTest.java
index c814cc5..c8a93a6 100644
--- a/tests/unit/java/com/android/server/VpnManagerServiceTest.java
+++ b/tests/unit/java/com/android/server/VpnManagerServiceTest.java
@@ -22,7 +22,11 @@
 import static com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 import static com.android.testutils.MiscAsserts.assertThrows;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
@@ -44,10 +48,14 @@
 import android.os.Looper;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.security.Credentials;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.net.VpnProfile;
 import com.android.server.connectivity.Vpn;
+import com.android.server.connectivity.VpnProfileStore;
+import com.android.server.net.LockdownVpnTracker;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRunner;
 import com.android.testutils.HandlerUtils;
@@ -60,6 +68,9 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
 @RunWith(DevSdkIgnoreRunner.class)
 @IgnoreUpTo(R) // VpnManagerService is not available before R
 @SmallTest
@@ -79,6 +90,8 @@
     @Mock private UserManager mUserManager;
     @Mock private INetd mNetd;
     @Mock private PackageManager mPackageManager;
+    @Mock private VpnProfileStore mVpnProfileStore;
+    @Mock private LockdownVpnTracker mLockdownVpnTracker;
 
     private VpnManagerServiceDependencies mDeps;
     private VpnManagerService mService;
@@ -107,6 +120,17 @@
                 INetd netd, @UserIdInt int userId) {
             return mVpn;
         }
+
+        @Override
+        public VpnProfileStore getVpnProfileStore() {
+            return mVpnProfileStore;
+        }
+
+        @Override
+        public LockdownVpnTracker createLockDownVpnTracker(Context context, Handler handler,
+                Vpn vpn, VpnProfile profile) {
+            return mLockdownVpnTracker;
+        }
     }
 
     @Before
@@ -203,10 +227,14 @@
     }
 
     private void sendIntent(Intent intent) {
+        sendIntent(mIntentReceiver, mContext, intent);
+    }
+
+    private void sendIntent(BroadcastReceiver receiver, Context context, Intent intent) {
         final Handler h = mHandlerThread.getThreadHandler();
 
         // Send in handler thread.
-        h.post(() -> mIntentReceiver.onReceive(mContext, intent));
+        h.post(() -> receiver.onReceive(context, intent));
         HandlerUtils.waitForIdle(mHandlerThread, TIMEOUT_MS);
     }
 
@@ -215,6 +243,21 @@
                 null /* packageName */, userId, -1 /* uid */, false /* isReplacing */));
     }
 
+    private void onUserUnlocked(int userId) {
+        sendIntent(buildIntent(Intent.ACTION_USER_UNLOCKED,
+                null /* packageName */, userId, -1 /* uid */, false /* isReplacing */));
+    }
+
+    private void onUserStopped(int userId) {
+        sendIntent(buildIntent(Intent.ACTION_USER_STOPPED,
+                null /* packageName */, userId, -1 /* uid */, false /* isReplacing */));
+    }
+
+    private void onLockDownReset() {
+        sendIntent(buildIntent(LockdownVpnTracker.ACTION_LOCKDOWN_RESET, null /* packageName */,
+                UserHandle.USER_SYSTEM, -1 /* uid */, false /* isReplacing */));
+    }
+
     private void onPackageAdded(String packageName, int userId, int uid, boolean isReplacing) {
         sendIntent(buildIntent(Intent.ACTION_PACKAGE_ADDED, packageName, userId, uid, isReplacing));
     }
@@ -241,4 +284,111 @@
         assertThrows(IllegalStateException.class, () ->
                 mUserPresentReceiver.onReceive(mContext, new Intent(Intent.ACTION_USER_PRESENT)));
     }
+
+    private void setupLockdownVpn(String packageName) {
+        final byte[] profileTag = packageName.getBytes(StandardCharsets.UTF_8);
+        doReturn(profileTag).when(mVpnProfileStore).get(Credentials.LOCKDOWN_VPN);
+    }
+
+    private void setupVpnProfile(String profileName) {
+        final VpnProfile profile = new VpnProfile(profileName);
+        profile.name = profileName;
+        profile.server = "192.0.2.1";
+        profile.dnsServers = "8.8.8.8";
+        profile.type = VpnProfile.TYPE_IPSEC_XAUTH_PSK;
+        final byte[] encodedProfile = profile.encode();
+        doReturn(encodedProfile).when(mVpnProfileStore).get(Credentials.VPN + profileName);
+    }
+
+    @Test
+    public void testUserPresent() {
+        // Verify that LockDownVpnTracker is not created.
+        verify(mLockdownVpnTracker, never()).init();
+
+        setupLockdownVpn(TEST_VPN_PKG);
+        setupVpnProfile(TEST_VPN_PKG);
+
+        // mUserPresentReceiver only registers ACTION_USER_PRESENT intent and does no verification
+        // on action, so an empty intent is enough.
+        sendIntent(mUserPresentReceiver, mSystemContext, new Intent());
+
+        verify(mLockdownVpnTracker).init();
+        verify(mSystemContext).unregisterReceiver(mUserPresentReceiver);
+        verify(mUserAllContext, never()).unregisterReceiver(any());
+    }
+
+    @Test
+    public void testUpdateLockdownVpn() {
+        setupLockdownVpn(TEST_VPN_PKG);
+        onUserUnlocked(SYSTEM_USER_ID);
+
+        // Will not create lockDownVpnTracker w/o valid profile configured in the keystore
+        verify(mLockdownVpnTracker, never()).init();
+
+        setupVpnProfile(TEST_VPN_PKG);
+
+        // Remove the user from mVpns
+        onUserStopped(SYSTEM_USER_ID);
+        onUserUnlocked(SYSTEM_USER_ID);
+        verify(mLockdownVpnTracker, never()).init();
+
+        // Add user back
+        onUserStarted(SYSTEM_USER_ID);
+        verify(mLockdownVpnTracker).init();
+
+        // Trigger another update. The existing LockDownVpnTracker should be shut down and
+        // initialize another one.
+        onUserUnlocked(SYSTEM_USER_ID);
+        verify(mLockdownVpnTracker).shutdown();
+        verify(mLockdownVpnTracker, times(2)).init();
+    }
+
+    @Test
+    public void testLockdownReset() {
+        // Init LockdownVpnTracker
+        setupLockdownVpn(TEST_VPN_PKG);
+        setupVpnProfile(TEST_VPN_PKG);
+        onUserUnlocked(SYSTEM_USER_ID);
+        verify(mLockdownVpnTracker).init();
+
+        onLockDownReset();
+        verify(mLockdownVpnTracker).reset();
+    }
+
+    @Test
+    public void testLockdownResetWhenLockdownVpnTrackerIsNotInit() {
+        setupLockdownVpn(TEST_VPN_PKG);
+        setupVpnProfile(TEST_VPN_PKG);
+
+        onLockDownReset();
+
+        // LockDownVpnTracker is not created. Lockdown reset will not take effect.
+        verify(mLockdownVpnTracker, never()).reset();
+    }
+
+    @Test
+    public void testIsVpnLockdownEnabled() {
+        // Vpn is created but the VPN lockdown is not enabled.
+        assertFalse(mService.isVpnLockdownEnabled(SYSTEM_USER_ID));
+
+        // Set lockdown for the SYSTEM_USER_ID VPN.
+        doReturn(true).when(mVpn).getLockdown();
+        assertTrue(mService.isVpnLockdownEnabled(SYSTEM_USER_ID));
+
+        // Even lockdown is enabled but no Vpn is created for SECONDARY_USER.
+        assertFalse(mService.isVpnLockdownEnabled(SECONDARY_USER.id));
+    }
+
+    @Test
+    public void testGetVpnLockdownAllowlist() {
+        doReturn(null).when(mVpn).getLockdownAllowlist();
+        assertNull(mService.getVpnLockdownAllowlist(SYSTEM_USER_ID));
+
+        final List<String> expected = List.of(PKGS);
+        doReturn(expected).when(mVpn).getLockdownAllowlist();
+        assertEquals(expected, mService.getVpnLockdownAllowlist(SYSTEM_USER_ID));
+
+        // Even lockdown is enabled but no Vpn is created for SECONDARY_USER.
+        assertNull(mService.getVpnLockdownAllowlist(SECONDARY_USER.id));
+    }
 }
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index 18e074c..f64e35b 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -862,7 +862,7 @@
         incrementCurrentTime(MINUTE_IN_MILLIS);
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
                 .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
-                        12L, 18L, 14L, 1L, 0L)));
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 12L, 18L, 14L, 1L, 0L)));
         forcePollAndWaitForIdle();
 
         // Verify 3g templates gets stats.
@@ -877,10 +877,10 @@
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
                 // Append more traffic on existing 3g stats entry.
                 .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
-                        16L, 22L, 17L, 2L, 0L))
+                         METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 16L, 22L, 17L, 2L, 0L))
                 // Add entry that is new on 4g.
                 .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE,
-                        33L, 27L, 8L, 10L, 1L)));
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 33L, 27L, 8L, 10L, 1L)));
         forcePollAndWaitForIdle();
 
         // Verify ALL_MOBILE template gets all. 3g template counters do not increase.
@@ -897,12 +897,12 @@
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
                 // Existing stats remains.
                 .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
-                        16L, 22L, 17L, 2L, 0L))
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 16L, 22L, 17L, 2L, 0L))
                 .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE,
-                        33L, 27L, 8L, 10L, 1L))
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 33L, 27L, 8L, 10L, 1L))
                 // Add some traffic on 5g.
                 .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
-                5L, 13L, 31L, 9L, 2L)));
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 5L, 13L, 31L, 9L, 2L)));
         forcePollAndWaitForIdle();
 
         // Verify ALL_MOBILE template gets all.
@@ -984,7 +984,7 @@
         incrementCurrentTime(MINUTE_IN_MILLIS);
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
                 .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
-                        36L, 41L, 24L, 96L, 0L)));
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 36L, 41L, 24L, 96L, 0L)));
         forcePollAndWaitForIdle();
 
         // OEM_PRIVATE network comes online.
@@ -999,7 +999,7 @@
         incrementCurrentTime(MINUTE_IN_MILLIS);
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
                 .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
-                        49L, 71L, 72L, 48L, 0L)));
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 49L, 71L, 72L, 48L, 0L)));
         forcePollAndWaitForIdle();
 
         // OEM_PAID + OEM_PRIVATE network comes online.
@@ -1015,7 +1015,7 @@
         incrementCurrentTime(MINUTE_IN_MILLIS);
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
                 .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
-                        57L, 86L, 83L, 93L, 0L)));
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 57L, 86L, 83L, 93L, 0L)));
         forcePollAndWaitForIdle();
 
         // OEM_NONE network comes online.
@@ -1029,7 +1029,7 @@
         incrementCurrentTime(MINUTE_IN_MILLIS);
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
                 .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
-                        29L, 73L, 34L, 31L, 0L)));
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 29L, 73L, 34L, 31L, 0L)));
         forcePollAndWaitForIdle();
 
         // Verify OEM_PAID template gets only relevant stats.
@@ -1140,7 +1140,8 @@
         // Increase arbitrary time which does not align to the bucket edge, create some traffic.
         incrementCurrentTime(1751000L);
         NetworkStats.Entry entry = new NetworkStats.Entry(
-                TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 50L, 5L, 51L, 1L, 3L);
+                TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 50L, 5L, 51L, 1L, 3L);
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1).insertEntry(entry));
         expectNetworkStatsUidDetail(buildEmptyStats());
         forcePollAndWaitForIdle();
@@ -1174,11 +1175,14 @@
                 new UnderlyingNetworkInfo[0]);
 
         NetworkStats.Entry entry1 = new NetworkStats.Entry(
-                TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L);
+                TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 50L, 5L, 50L, 5L, 0L);
         NetworkStats.Entry entry2 = new NetworkStats.Entry(
-                TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 50L, 5L, 50L, 5L, 0L);
+                TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 50L, 5L, 50L, 5L, 0L);
         NetworkStats.Entry entry3 = new NetworkStats.Entry(
-                TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xBEEF, 1024L, 8L, 512L, 4L, 0L);
+                TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xBEEF, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 1024L, 8L, 512L, 4L, 0L);
 
         incrementCurrentTime(HOUR_IN_MILLIS);
         expectDefaultSettings();
@@ -1686,7 +1690,7 @@
         incrementCurrentTime(MINUTE_IN_MILLIS);
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
                 .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
-                        12L, 18L, 14L, 1L, 0L)));
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 12L, 18L, 14L, 1L, 0L)));
         forcePollAndWaitForIdle();
 
         // Since CombineSubtypeEnabled is false by default in unit test, the generated traffic
@@ -1710,9 +1714,10 @@
         // Append more traffic on existing snapshot.
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
                 .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
-                        12L + 4L, 18L + 4L, 14L + 3L, 1L + 1L, 0L))
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 12L + 4L, 18L + 4L, 14L + 3L,
+                        1L + 1L, 0L))
                 .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE,
-                        35L, 29L, 7L, 11L, 1L)));
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 35L, 29L, 7L, 11L, 1L)));
         forcePollAndWaitForIdle();
 
         // Verify 3G counters do not increase, while template with unknown RAT type gets new
@@ -1732,9 +1737,9 @@
         // Append more traffic on existing snapshot.
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
                 .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
-                        22L, 26L, 19L, 5L, 0L))
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 22L, 26L, 19L, 5L, 0L))
                 .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE,
-                        35L, 29L, 7L, 11L, 1L)));
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 35L, 29L, 7L, 11L, 1L)));
         forcePollAndWaitForIdle();
 
         // Verify traffic is split by RAT type, no increase on template with unknown RAT type