Merge changes I1cf472bd,Ibdaf2b23,I6967ad6f,I65f522c2,I0f220d1f, ...

* changes:
  Make the temporary NC sanitized instead of inlining
  Remove the legacy int from FullScore.
  Remove accessors for legacy int score from NAI
  Log the creation time of network agents
  Show time taken by the rematch
  Address some IDE warnings in ConnectivityService
  Copy arguments from NetworkAgent registration early
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/dscpPolicy.c b/bpf_progs/dscpPolicy.c
index f308931..08fbcb1 100644
--- a/bpf_progs/dscpPolicy.c
+++ b/bpf_progs/dscpPolicy.c
@@ -121,10 +121,13 @@
 
     RuleEntry* existing_rule = bpf_socket_policy_cache_map_lookup_elem(&cookie);
 
-    if (existing_rule && v6_equal(src_ip, existing_rule->src_ip) &&
-            v6_equal(dst_ip, existing_rule->dst_ip) && skb->ifindex == existing_rule->ifindex &&
-        ntohs(sport) == htons(existing_rule->src_port) &&
-        ntohs(dport) == htons(existing_rule->dst_port) && protocol == existing_rule->proto) {
+    if (existing_rule &&
+        v6_equal(src_ip, existing_rule->src_ip) &&
+        v6_equal(dst_ip, existing_rule->dst_ip) &&
+        skb->ifindex == existing_rule->ifindex &&
+        sport == existing_rule->src_port &&
+        dport == existing_rule->dst_port &&
+        protocol == existing_rule->proto) {
         if (existing_rule->dscp_val < 0) return;
         if (ipv4) {
             uint8_t newTos = UPDATE_TOS(existing_rule->dscp_val, tos);
@@ -145,8 +148,6 @@
     int8_t new_dscp = -1;
 
     for (register uint64_t i = 0; i < MAX_POLICIES; i++) {
-        int score = 0;
-        uint8_t temp_mask = 0;
         // Using a uint64 in for loop prevents infinite loop during BPF load,
         // but the key is uint32, so convert back.
         uint32_t key = i;
@@ -158,38 +159,35 @@
             policy = bpf_ipv6_dscp_policies_map_lookup_elem(&key);
         }
 
-        // If the policy lookup failed, present_fields is 0, or iface index does not match
-        // index on skb buff, then we can continue to next policy.
-        if (!policy || policy->present_fields == 0 || policy->ifindex != skb->ifindex) continue;
+        // If the policy lookup failed, just continue (this should not ever happen)
+        if (!policy) continue;
 
-        if ((policy->present_fields & SRC_IP_MASK_FLAG) == SRC_IP_MASK_FLAG &&
-            v6_equal(src_ip, policy->src_ip)) {
-            score++;
-            temp_mask |= SRC_IP_MASK_FLAG;
-        }
-        if ((policy->present_fields & DST_IP_MASK_FLAG) == DST_IP_MASK_FLAG &&
-            v6_equal(dst_ip, policy->dst_ip)) {
-            score++;
-            temp_mask |= DST_IP_MASK_FLAG;
-        }
-        if ((policy->present_fields & SRC_PORT_MASK_FLAG) == SRC_PORT_MASK_FLAG &&
-            ntohs(sport) == htons(policy->src_port)) {
-            score++;
-            temp_mask |= SRC_PORT_MASK_FLAG;
-        }
-        if ((policy->present_fields & DST_PORT_MASK_FLAG) == DST_PORT_MASK_FLAG &&
-            ntohs(dport) >= htons(policy->dst_port_start) &&
-            ntohs(dport) <= htons(policy->dst_port_end)) {
-            score++;
-            temp_mask |= DST_PORT_MASK_FLAG;
-        }
-        if ((policy->present_fields & PROTO_MASK_FLAG) == PROTO_MASK_FLAG &&
-            protocol == policy->proto) {
-            score++;
-            temp_mask |= PROTO_MASK_FLAG;
-        }
+        // If policy iface index does not match skb, then skip to next policy.
+        if (policy->ifindex != skb->ifindex) continue;
 
-        if (score > best_score && temp_mask == policy->present_fields) {
+        int score = 0;
+
+        if (policy->present_fields & PROTO_MASK_FLAG) {
+            if (protocol != policy->proto) continue;
+            score += 0xFFFF;
+        }
+        if (policy->present_fields & SRC_IP_MASK_FLAG) {
+            if (v6_not_equal(src_ip, policy->src_ip)) continue;
+            score += 0xFFFF;
+        }
+        if (policy->present_fields & DST_IP_MASK_FLAG) {
+            if (v6_not_equal(dst_ip, policy->dst_ip)) continue;
+            score += 0xFFFF;
+        }
+        if (policy->present_fields & SRC_PORT_MASK_FLAG) {
+            if (sport != policy->src_port) continue;
+            score += 0xFFFF;
+        }
+        if (ntohs(dport) < policy->dst_port_start) continue;
+        if (ntohs(dport) > policy->dst_port_end) continue;
+        score += 0xFFFF + policy->dst_port_start - policy->dst_port_end;
+
+        if (score > best_score) {
             best_score = score;
             new_dscp = policy->dscp_val;
         }
diff --git a/bpf_progs/dscpPolicy.h b/bpf_progs/dscpPolicy.h
index c1db6ab..9680670 100644
--- a/bpf_progs/dscpPolicy.h
+++ b/bpf_progs/dscpPolicy.h
@@ -20,16 +20,22 @@
 #define SRC_IP_MASK_FLAG     1
 #define DST_IP_MASK_FLAG     2
 #define SRC_PORT_MASK_FLAG   4
-#define DST_PORT_MASK_FLAG   8
-#define PROTO_MASK_FLAG      16
+#define PROTO_MASK_FLAG      8
 
 #define STRUCT_SIZE(name, size) _Static_assert(sizeof(name) == (size), "Incorrect struct size.")
 
-#define v6_equal(a, b) \
-    (((a.s6_addr32[0] ^ b.s6_addr32[0]) | \
-      (a.s6_addr32[1] ^ b.s6_addr32[1]) | \
-      (a.s6_addr32[2] ^ b.s6_addr32[2]) | \
-      (a.s6_addr32[3] ^ b.s6_addr32[3])) == 0)
+// Retrieve the first (ie. high) 64 bits of an IPv6 address (in network order)
+#define v6_hi_be64(v) (*(uint64_t*)&((v).s6_addr32[0]))
+
+// Retrieve the last (ie. low) 64 bits of an IPv6 address (in network order)
+#define v6_lo_be64(v) (*(uint64_t*)&((v).s6_addr32[2]))
+
+// This returns a non-zero u64 iff a != b
+#define v6_not_equal(a, b) ((v6_hi_be64(a) ^ v6_hi_be64(b)) \
+                          | (v6_lo_be64(a) ^ v6_lo_be64(b)))
+
+// Returns 'a == b' as boolean
+#define v6_equal(a, b) (!v6_not_equal((a), (b)))
 
 // TODO: these are already defined in packages/modules/Connectivity/bpf_progs/bpf_net_helpers.h.
 // smove to common location in future.
@@ -47,8 +53,8 @@
     struct in6_addr dst_ip;
     uint32_t ifindex;
     __be16 src_port;
-    __be16 dst_port_start;
-    __be16 dst_port_end;
+    uint16_t dst_port_start;
+    uint16_t dst_port_end;
     uint8_t proto;
     int8_t dscp_val;  // -1 none, or 0..63 DSCP value
     uint8_t present_fields;
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index 44f76de..10559dd 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -85,10 +85,18 @@
 DEFINE_BPF_MAP_NO_NETD(iface_index_name_map, HASH, uint32_t, IfaceValue, IFACE_INDEX_NAME_MAP_SIZE)
 
 // iptables xt_bpf programs need to be usable by both netd and netutils_wrappers
+// selinux contexts, because even non-xt_bpf iptables mutations are implemented as
+// a full table dump, followed by an update in userspace, and then a reload into the kernel,
+// where any already in-use xt_bpf matchers are serialized as the path to the pinned
+// program (see XT_BPF_MODE_PATH_PINNED) and then the iptables binary (or rather
+// the kernel acting on behalf of it) must be able to retrieve the pinned program
+// for the reload to succeed
 #define DEFINE_XTBPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \
     DEFINE_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog)
 
 // programs that need to be usable by netd, but not by netutils_wrappers
+// (this is because these are currently attached by the mainline provided libnetd_updatable .so
+// which is loaded into netd and thus runs as netd uid/gid/selinux context)
 #define DEFINE_NETD_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \
     DEFINE_BPF_PROG_EXT(SECTION_NAME, prog_uid, prog_gid, the_prog, \
                         KVER_NONE, KVER_INF, false, "fs_bpf_netd_readonly", "")
@@ -432,4 +440,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 77931b1..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;
@@ -53,6 +56,7 @@
 import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_XT;
 import static android.os.Trace.TRACE_TAG_NETWORK;
 import static android.system.OsConstants.ENOENT;
+import static android.system.OsConstants.R_OK;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
@@ -130,6 +134,7 @@
 import android.service.NetworkInterfaceProto;
 import android.service.NetworkStatsServiceDumpProto;
 import android.system.ErrnoException;
+import android.system.Os;
 import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionPlan;
 import android.text.TextUtils;
@@ -156,6 +161,7 @@
 import com.android.net.module.util.LocationPermissionChecker;
 import com.android.net.module.util.NetworkStatsUtils;
 import com.android.net.module.util.PermissionUtils;
+import com.android.net.module.util.Struct;
 import com.android.net.module.util.Struct.U32;
 import com.android.net.module.util.Struct.U8;
 import com.android.net.module.util.bpf.CookieTagMapKey;
@@ -2698,6 +2704,23 @@
                 mUidTagRecorder.dumpLocked(pw, fullHistory);
                 pw.decreaseIndent();
             }
+
+            pw.println();
+            pw.println("BPF map status:");
+            pw.increaseIndent();
+            dumpMapStatus(pw);
+            pw.decreaseIndent();
+            pw.println();
+
+            // Following BPF map content dump contains uid and tag regardless of the flags because
+            // following dumps are moved from TrafficController and bug report already contains this
+            // information.
+            pw.println("BPF map content:");
+            pw.increaseIndent();
+            dumpCookieTagMapLocked(pw);
+            dumpUidCounterSetMapLocked(pw);
+            dumpAppUidStatsMapLocked(pw);
+            pw.decreaseIndent();
         }
     }
 
@@ -2732,6 +2755,102 @@
         }
     }
 
+    private <K extends Struct, V extends Struct> String getMapStatus(
+            final IBpfMap<K, V> map, final String path) {
+        if (map != null) {
+            return "OK";
+        }
+        try {
+            Os.access(path, R_OK);
+            return "NULL(map is pinned to " + path + ")";
+        } catch (ErrnoException e) {
+            return "NULL(map is not pinned to " + path + ": " + Os.strerror(e.errno) + ")";
+        }
+    }
+
+    private void dumpMapStatus(final IndentingPrintWriter pw) {
+        pw.println("mCookieTagMap: " + getMapStatus(mCookieTagMap, COOKIE_TAG_MAP_PATH));
+        pw.println("mUidCounterSetMap: "
+                + getMapStatus(mUidCounterSetMap, UID_COUNTERSET_MAP_PATH));
+        pw.println("mAppUidStatsMap: " + getMapStatus(mAppUidStatsMap, APP_UID_STATS_MAP_PATH));
+    }
+
+    @GuardedBy("mStatsLock")
+    private void dumpCookieTagMapLocked(final IndentingPrintWriter pw) {
+        if (mCookieTagMap == null) {
+            return;
+        }
+        pw.println("mCookieTagMap:");
+        pw.increaseIndent();
+        try {
+            mCookieTagMap.forEach((key, value) -> {
+                // value could be null if there is a concurrent entry deletion.
+                // http://b/220084230.
+                if (value != null) {
+                    pw.println("cookie=" + key.socketCookie
+                            + " tag=0x" + Long.toHexString(value.tag)
+                            + " uid=" + value.uid);
+                } else {
+                    pw.println("Entry is deleted while dumping, iterating from first entry");
+                }
+            });
+        } catch (ErrnoException e) {
+            pw.println("mCookieTagMap dump end with error: " + Os.strerror(e.errno));
+        }
+        pw.decreaseIndent();
+    }
+
+    @GuardedBy("mStatsLock")
+    private void dumpUidCounterSetMapLocked(final IndentingPrintWriter pw) {
+        if (mUidCounterSetMap == null) {
+            return;
+        }
+        pw.println("mUidCounterSetMap:");
+        pw.increaseIndent();
+        try {
+            mUidCounterSetMap.forEach((uid, set) -> {
+                // set could be null if there is a concurrent entry deletion.
+                // http://b/220084230.
+                if (set != null) {
+                    pw.println("uid=" + uid.val + " set=" + set.val);
+                } else {
+                    pw.println("Entry is deleted while dumping, iterating from first entry");
+                }
+            });
+        } catch (ErrnoException e) {
+            pw.println("mUidCounterSetMap dump end with error: " + Os.strerror(e.errno));
+        }
+        pw.decreaseIndent();
+    }
+
+    @GuardedBy("mStatsLock")
+    private void dumpAppUidStatsMapLocked(final IndentingPrintWriter pw) {
+        if (mAppUidStatsMap == null) {
+            return;
+        }
+        pw.println("mAppUidStatsMap:");
+        pw.increaseIndent();
+        pw.println("uid rxBytes rxPackets txBytes txPackets");
+        try {
+            mAppUidStatsMap.forEach((key, value) -> {
+                // value could be null if there is a concurrent entry deletion.
+                // http://b/220084230.
+                if (value != null) {
+                    pw.println(key.uid + " "
+                            + value.rxBytes + " "
+                            + value.rxPackets + " "
+                            + value.txBytes + " "
+                            + value.txPackets);
+                } else {
+                    pw.println("Entry is deleted while dumping, iterating from first entry");
+                }
+            });
+        } catch (ErrnoException e) {
+            pw.println("mAppUidStatsMap dump end with error: " + Os.strerror(e.errno));
+        }
+        pw.decreaseIndent();
+    }
+
     private NetworkStats readNetworkStatsSummaryDev() {
         try {
             return mStatsFactory.readNetworkStatsSummaryDev();
@@ -2806,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/native/TrafficController.cpp b/service/native/TrafficController.cpp
index 4dc056d..71329ad 100644
--- a/service/native/TrafficController.cpp
+++ b/service/native/TrafficController.cpp
@@ -173,13 +173,8 @@
     RETURN_IF_NOT_OK(mIfaceStatsMap.init(IFACE_STATS_MAP_PATH));
 
     RETURN_IF_NOT_OK(mConfigurationMap.init(CONFIGURATION_MAP_PATH));
-    RETURN_IF_NOT_OK(
-            mConfigurationMap.writeValue(UID_RULES_CONFIGURATION_KEY, DEFAULT_CONFIG, BPF_ANY));
-    RETURN_IF_NOT_OK(mConfigurationMap.writeValue(CURRENT_STATS_MAP_CONFIGURATION_KEY, SELECT_MAP_A,
-                                                  BPF_ANY));
 
     RETURN_IF_NOT_OK(mUidOwnerMap.init(UID_OWNER_MAP_PATH));
-    RETURN_IF_NOT_OK(mUidOwnerMap.clear());
     RETURN_IF_NOT_OK(mUidPermissionMap.init(UID_PERMISSION_MAP_PATH));
     ALOGI("%s successfully", __func__);
 
@@ -617,12 +612,6 @@
     ScopedIndent indentPreBpfModule(dw);
 
     dw.blankline();
-    dw.println("mCookieTagMap status: %s",
-               getMapStatus(mCookieTagMap.getMap(), COOKIE_TAG_MAP_PATH).c_str());
-    dw.println("mUidCounterSetMap status: %s",
-               getMapStatus(mUidCounterSetMap.getMap(), UID_COUNTERSET_MAP_PATH).c_str());
-    dw.println("mAppUidStatsMap status: %s",
-               getMapStatus(mAppUidStatsMap.getMap(), APP_UID_STATS_MAP_PATH).c_str());
     dw.println("mStatsMapA status: %s",
                getMapStatus(mStatsMapA.getMap(), STATS_MAP_A_PATH).c_str());
     dw.println("mStatsMapB status: %s",
@@ -658,44 +647,6 @@
 
     ScopedIndent indentForMapContent(dw);
 
-    // Print CookieTagMap content.
-    dumpBpfMap("mCookieTagMap", dw, "");
-    const auto printCookieTagInfo = [&dw](const uint64_t& key, const UidTagValue& value,
-                                          const BpfMap<uint64_t, UidTagValue>&) {
-        dw.println("cookie=%" PRIu64 " tag=0x%x uid=%u", key, value.tag, value.uid);
-        return base::Result<void>();
-    };
-    base::Result<void> res = mCookieTagMap.iterateWithValue(printCookieTagInfo);
-    if (!res.ok()) {
-        dw.println("mCookieTagMap print end with error: %s", res.error().message().c_str());
-    }
-
-    // Print UidCounterSetMap content.
-    dumpBpfMap("mUidCounterSetMap", dw, "");
-    const auto printUidInfo = [&dw](const uint32_t& key, const uint8_t& value,
-                                    const BpfMap<uint32_t, uint8_t>&) {
-        dw.println("%u %u", key, value);
-        return base::Result<void>();
-    };
-    res = mUidCounterSetMap.iterateWithValue(printUidInfo);
-    if (!res.ok()) {
-        dw.println("mUidCounterSetMap print end with error: %s", res.error().message().c_str());
-    }
-
-    // Print AppUidStatsMap content.
-    std::string appUidStatsHeader = StringPrintf("uid rxBytes rxPackets txBytes txPackets");
-    dumpBpfMap("mAppUidStatsMap:", dw, appUidStatsHeader);
-    auto printAppUidStatsInfo = [&dw](const uint32_t& key, const StatsValue& value,
-                                      const BpfMap<uint32_t, StatsValue>&) {
-        dw.println("%u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, key, value.rxBytes,
-                   value.rxPackets, value.txBytes, value.txPackets);
-        return base::Result<void>();
-    };
-    res = mAppUidStatsMap.iterateWithValue(printAppUidStatsInfo);
-    if (!res.ok()) {
-        dw.println("mAppUidStatsMap print end with error: %s", res.error().message().c_str());
-    }
-
     // Print uidStatsMap content.
     std::string statsHeader = StringPrintf("ifaceIndex ifaceName tag_hex uid_int cnt_set rxBytes"
                                            " rxPackets txBytes txPackets");
@@ -712,7 +663,7 @@
                    value.rxPackets, value.txBytes, value.txPackets);
         return base::Result<void>();
     };
-    res = mStatsMapA.iterateWithValue(printStatsInfo);
+    base::Result<void> res = mStatsMapA.iterateWithValue(printStatsInfo);
     if (!res.ok()) {
         dw.println("mStatsMapA print end with error: %s", res.error().message().c_str());
     }
diff --git a/service/native/TrafficControllerTest.cpp b/service/native/TrafficControllerTest.cpp
index 7730c13..256a2a0 100644
--- a/service/native/TrafficControllerTest.cpp
+++ b/service/native/TrafficControllerTest.cpp
@@ -791,13 +791,6 @@
     // ifaceIndex ifaceName tag_hex uid_int cnt_set rxBytes rxPackets txBytes txPackets
     // 999 test0 0x2a 10086 1 100 1 0 0
     std::vector<std::string> expectedLines = {
-        "mCookieTagMap:",
-        fmt::format("cookie={} tag={:#x} uid={}", TEST_COOKIE, TEST_TAG, TEST_UID),
-        "mUidCounterSetMap:",
-        fmt::format("{} {}", TEST_UID3, TEST_COUNTERSET),
-        "mAppUidStatsMap::",  // TODO@: fix double colon
-        "uid rxBytes rxPackets txBytes txPackets",
-        fmt::format("{} {} {} {} {}", TEST_UID, RXBYTES, RXPACKETS, TXBYTES, TXPACKETS),
         "mStatsMapA",
         "ifaceIndex ifaceName tag_hex uid_int cnt_set rxBytes rxPackets txBytes txPackets",
         fmt::format("{} {} {:#x} {} {} {} {} {} {}",
@@ -833,9 +826,6 @@
             "Read value of map -1 failed: Bad file descriptor";
 
     std::vector<std::string> expectedLines = {
-        fmt::format("mCookieTagMap {}", kErrIterate),
-        fmt::format("mUidCounterSetMap {}", kErrIterate),
-        fmt::format("mAppUidStatsMap {}", kErrIterate),
         fmt::format("mStatsMapA {}", kErrIterate),
         fmt::format("mStatsMapB {}", kErrIterate),
         fmt::format("mIfaceIndexNameMap {}", kErrIterate),
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index 0270fc2..594223c 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -40,7 +40,6 @@
 import android.system.Os;
 import android.util.Log;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.modules.utils.build.SdkLevel;
 import com.android.net.module.util.BpfMap;
@@ -87,6 +86,11 @@
     private static final String UID_OWNER_MAP_PATH =
             "/sys/fs/bpf/netd_shared/map_netd_uid_owner_map";
     private static final U32 UID_RULES_CONFIGURATION_KEY = new U32(0);
+    private static final U32 CURRENT_STATS_MAP_CONFIGURATION_KEY = new U32(1);
+    private static final long UID_RULES_DEFAULT_CONFIGURATION = 0;
+    private static final long STATS_SELECT_MAP_A = 0;
+    private static final long STATS_SELECT_MAP_B = 1;
+
     private static BpfMap<U32, U32> sConfigurationMap = null;
     // BpfMap for UID_OWNER_MAP_PATH. This map is not accessed by others.
     private static BpfMap<U32, UidOwnerValue> sUidOwnerMap = null;
@@ -149,13 +153,31 @@
         }
     }
 
-    private static void setBpfMaps() {
+    private static void initBpfMaps() {
         if (sConfigurationMap == null) {
             sConfigurationMap = getConfigurationMap();
         }
+        try {
+            sConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY,
+                    new U32(UID_RULES_DEFAULT_CONFIGURATION));
+        } catch (ErrnoException e) {
+            throw new IllegalStateException("Failed to initialize uid rules configuration", e);
+        }
+        try {
+            sConfigurationMap.updateEntry(CURRENT_STATS_MAP_CONFIGURATION_KEY,
+                    new U32(STATS_SELECT_MAP_A));
+        } catch (ErrnoException e) {
+            throw new IllegalStateException("Failed to initialize current stats configuration", e);
+        }
+
         if (sUidOwnerMap == null) {
             sUidOwnerMap = getUidOwnerMap();
         }
+        try {
+            sUidOwnerMap.clear();
+        } catch (ErrnoException e) {
+            throw new IllegalStateException("Failed to initialize uid owner map", e);
+        }
     }
 
     /**
@@ -171,7 +193,7 @@
         }
         Log.d(TAG, "BpfNetMaps is initialized with sEnableJavaBpfMap=" + sEnableJavaBpfMap);
 
-        setBpfMaps();
+        initBpfMaps();
         native_init();
         sInitialized = true;
     }
@@ -342,7 +364,13 @@
      */
     public void addNaughtyApp(final int uid) {
         throwIfPreT("addNaughtyApp is not available on pre-T devices");
-        addRule(uid, PENALTY_BOX_MATCH, "addNaughtyApp");
+
+        if (sEnableJavaBpfMap) {
+            addRule(uid, PENALTY_BOX_MATCH, "addNaughtyApp");
+        } else {
+            final int err = native_addNaughtyApp(uid);
+            maybeThrow(err, "Unable to add naughty app");
+        }
     }
 
     /**
@@ -354,7 +382,13 @@
      */
     public void removeNaughtyApp(final int uid) {
         throwIfPreT("removeNaughtyApp is not available on pre-T devices");
-        removeRule(uid, PENALTY_BOX_MATCH, "removeNaughtyApp");
+
+        if (sEnableJavaBpfMap) {
+            removeRule(uid, PENALTY_BOX_MATCH, "removeNaughtyApp");
+        } else {
+            final int err = native_removeNaughtyApp(uid);
+            maybeThrow(err, "Unable to remove naughty app");
+        }
     }
 
     /**
@@ -366,7 +400,13 @@
      */
     public void addNiceApp(final int uid) {
         throwIfPreT("addNiceApp is not available on pre-T devices");
-        addRule(uid, HAPPY_BOX_MATCH, "addNiceApp");
+
+        if (sEnableJavaBpfMap) {
+            addRule(uid, HAPPY_BOX_MATCH, "addNiceApp");
+        } else {
+            final int err = native_addNiceApp(uid);
+            maybeThrow(err, "Unable to add nice app");
+        }
     }
 
     /**
@@ -378,7 +418,13 @@
      */
     public void removeNiceApp(final int uid) {
         throwIfPreT("removeNiceApp is not available on pre-T devices");
-        removeRule(uid, HAPPY_BOX_MATCH, "removeNiceApp");
+
+        if (sEnableJavaBpfMap) {
+            removeRule(uid, HAPPY_BOX_MATCH, "removeNiceApp");
+        } else {
+            final int err = native_removeNiceApp(uid);
+            maybeThrow(err, "Unable to remove nice app");
+        }
     }
 
     /**
@@ -393,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");
         }
     }
 
@@ -440,36 +491,73 @@
     public void replaceUidChain(final int chain, final int[] uids) {
         throwIfPreT("replaceUidChain is not available on pre-T devices");
 
-        final long match;
-        try {
-            match = getMatchByFirewallChain(chain);
-        } catch (ServiceSpecificException e) {
-            // Throws IllegalArgumentException to keep the behavior of
-            // ConnectivityManager#replaceFirewallChain API
-            throw new IllegalArgumentException("Invalid firewall chain: " + chain);
-        }
-        final Set<Integer> uidSet = Arrays.stream(uids).boxed().collect(Collectors.toSet());
-        final Set<Integer> uidSetToRemoveRule = new HashSet<>();
-        try {
-            synchronized (sUidOwnerMap) {
-                sUidOwnerMap.forEach((uid, config) -> {
-                    // config could be null if there is a concurrent entry deletion.
-                    // http://b/220084230.
-                    if (config != null
-                            && !uidSet.contains((int) uid.val) && (config.rule & match) != 0) {
-                        uidSetToRemoveRule.add((int) uid.val);
-                    }
-                });
-
-                for (final int uid : uidSetToRemoveRule) {
-                    removeRule(uid, match, "replaceUidChain");
-                }
-                for (final int uid : uids) {
-                    addRule(uid, match, "replaceUidChain");
-                }
+        if (sEnableJavaBpfMap) {
+            final long match;
+            try {
+                match = getMatchByFirewallChain(chain);
+            } catch (ServiceSpecificException e) {
+                // Throws IllegalArgumentException to keep the behavior of
+                // ConnectivityManager#replaceFirewallChain API
+                throw new IllegalArgumentException("Invalid firewall chain: " + chain);
             }
-        } catch (ErrnoException | ServiceSpecificException e) {
-            Log.e(TAG, "replaceUidChain failed: " + e);
+            final Set<Integer> uidSet = Arrays.stream(uids).boxed().collect(Collectors.toSet());
+            final Set<Integer> uidSetToRemoveRule = new HashSet<>();
+            try {
+                synchronized (sUidOwnerMap) {
+                    sUidOwnerMap.forEach((uid, config) -> {
+                        // config could be null if there is a concurrent entry deletion.
+                        // http://b/220084230.
+                        if (config != null
+                                && !uidSet.contains((int) uid.val) && (config.rule & match) != 0) {
+                            uidSetToRemoveRule.add((int) uid.val);
+                        }
+                    });
+
+                    for (final int uid : uidSetToRemoveRule) {
+                        removeRule(uid, match, "replaceUidChain");
+                    }
+                    for (final int uid : uids) {
+                        addRule(uid, match, "replaceUidChain");
+                    }
+                }
+            } catch (ErrnoException | ServiceSpecificException e) {
+                Log.e(TAG, "replaceUidChain failed: " + e);
+            }
+        } else {
+            final int err;
+            switch (chain) {
+                case FIREWALL_CHAIN_DOZABLE:
+                    err = native_replaceUidChain("fw_dozable", true /* isAllowList */, uids);
+                    break;
+                case FIREWALL_CHAIN_STANDBY:
+                    err = native_replaceUidChain("fw_standby", false /* isAllowList */, uids);
+                    break;
+                case FIREWALL_CHAIN_POWERSAVE:
+                    err = native_replaceUidChain("fw_powersave", true /* isAllowList */, uids);
+                    break;
+                case FIREWALL_CHAIN_RESTRICTED:
+                    err = native_replaceUidChain("fw_restricted", true /* isAllowList */, uids);
+                    break;
+                case FIREWALL_CHAIN_LOW_POWER_STANDBY:
+                    err = native_replaceUidChain(
+                            "fw_low_power_standby", true /* isAllowList */, uids);
+                    break;
+                case FIREWALL_CHAIN_OEM_DENY_1:
+                    err = native_replaceUidChain("fw_oem_deny_1", false /* isAllowList */, uids);
+                    break;
+                case FIREWALL_CHAIN_OEM_DENY_2:
+                    err = native_replaceUidChain("fw_oem_deny_2", false /* isAllowList */, uids);
+                    break;
+                case FIREWALL_CHAIN_OEM_DENY_3:
+                    err = native_replaceUidChain("fw_oem_deny_3", false /* isAllowList */, uids);
+                    break;
+                default:
+                    throw new IllegalArgumentException("replaceFirewallChain with invalid chain: "
+                            + chain);
+            }
+            if (err != 0) {
+                Log.e(TAG, "replaceUidChain failed: " + Os.strerror(-err));
+            }
         }
     }
 
@@ -485,15 +573,20 @@
     public void setUidRule(final int childChain, final int uid, final int firewallRule) {
         throwIfPreT("setUidRule is not available on pre-T devices");
 
-        final long match = getMatchByFirewallChain(childChain);
-        final boolean isAllowList = isFirewallAllowList(childChain);
-        final boolean add = (firewallRule == FIREWALL_RULE_ALLOW && isAllowList)
-                || (firewallRule == FIREWALL_RULE_DENY && !isAllowList);
+        if (sEnableJavaBpfMap) {
+            final long match = getMatchByFirewallChain(childChain);
+            final boolean isAllowList = isFirewallAllowList(childChain);
+            final boolean add = (firewallRule == FIREWALL_RULE_ALLOW && isAllowList)
+                    || (firewallRule == FIREWALL_RULE_DENY && !isAllowList);
 
-        if (add) {
-            addRule(uid, match, "setUidRule");
+            if (add) {
+                addRule(uid, match, "setUidRule");
+            } else {
+                removeRule(uid, match, "setUidRule");
+            }
         } else {
-            removeRule(uid, match, "setUidRule");
+            final int err = native_setUidRule(childChain, uid, firewallRule);
+            maybeThrow(err, "Unable to set uid rule");
         }
     }
 
@@ -519,24 +612,30 @@
             mNetd.firewallAddUidInterfaceRules(ifName, uids);
             return;
         }
-        // Null ifName is a wildcard to allow apps to receive packets on all interfaces and ifIndex
-        // is set to 0.
-        final int ifIndex;
-        if (ifName == null) {
-            ifIndex = 0;
+
+        if (sEnableJavaBpfMap) {
+            // Null ifName is a wildcard to allow apps to receive packets on all interfaces and
+            // ifIndex is set to 0.
+            final int ifIndex;
+            if (ifName == null) {
+                ifIndex = 0;
+            } else {
+                ifIndex = mDeps.getIfIndex(ifName);
+                if (ifIndex == 0) {
+                    throw new ServiceSpecificException(ENODEV,
+                            "Failed to get index of interface " + ifName);
+                }
+            }
+            for (final int uid : uids) {
+                try {
+                    addRule(uid, IIF_MATCH, ifIndex, "addUidInterfaceRules");
+                } catch (ServiceSpecificException e) {
+                    Log.e(TAG, "addRule failed uid=" + uid + " ifName=" + ifName + ", " + e);
+                }
+            }
         } else {
-            ifIndex = mDeps.getIfIndex(ifName);
-            if (ifIndex == 0) {
-                throw new ServiceSpecificException(ENODEV,
-                        "Failed to get index of interface " + ifName);
-            }
-        }
-        for (final int uid: uids) {
-            try {
-                addRule(uid, IIF_MATCH, ifIndex, "addUidInterfaceRules");
-            } catch (ServiceSpecificException e) {
-                Log.e(TAG, "addRule failed uid=" + uid + " ifName=" + ifName + ", " + e);
-            }
+            final int err = native_addUidInterfaceRules(ifName, uids);
+            maybeThrow(err, "Unable to add uid interface rules");
         }
     }
 
@@ -556,12 +655,18 @@
             mNetd.firewallRemoveUidInterfaceRules(uids);
             return;
         }
-        for (final int uid: uids) {
-            try {
-                removeRule(uid, IIF_MATCH, "removeUidInterfaceRules");
-            } catch (ServiceSpecificException e) {
-                Log.e(TAG, "removeRule failed uid=" + uid + ", " + e);
+
+        if (sEnableJavaBpfMap) {
+            for (final int uid : uids) {
+                try {
+                    removeRule(uid, IIF_MATCH, "removeUidInterfaceRules");
+                } catch (ServiceSpecificException e) {
+                    Log.e(TAG, "removeRule failed uid=" + uid + ", " + e);
+                }
             }
+        } else {
+            final int err = native_removeUidInterfaceRules(uids);
+            maybeThrow(err, "Unable to remove uid interface rules");
         }
     }
 
@@ -575,10 +680,16 @@
      */
     public void updateUidLockdownRule(final int uid, final boolean add) {
         throwIfPreT("updateUidLockdownRule is not available on pre-T devices");
-        if (add) {
-            addRule(uid, LOCKDOWN_VPN_MATCH, "updateUidLockdownRule");
+
+        if (sEnableJavaBpfMap) {
+            if (add) {
+                addRule(uid, LOCKDOWN_VPN_MATCH, "updateUidLockdownRule");
+            } else {
+                removeRule(uid, LOCKDOWN_VPN_MATCH, "updateUidLockdownRule");
+            }
         } else {
-            removeRule(uid, LOCKDOWN_VPN_MATCH, "updateUidLockdownRule");
+            final int err = native_updateUidLockdownRule(uid, add);
+            maybeThrow(err, "Unable to update lockdown rule");
         }
     }
 
@@ -629,24 +740,15 @@
     }
 
     private static native void native_init();
-    @GuardedBy("sUidOwnerMap")
     private native int native_addNaughtyApp(int uid);
-    @GuardedBy("sUidOwnerMap")
     private native int native_removeNaughtyApp(int uid);
-    @GuardedBy("sUidOwnerMap")
     private native int native_addNiceApp(int uid);
-    @GuardedBy("sUidOwnerMap")
     private native int native_removeNiceApp(int uid);
     private native int native_setChildChain(int childChain, boolean enable);
-    @GuardedBy("sUidOwnerMap")
     private native int native_replaceUidChain(String name, boolean isAllowlist, int[] uids);
-    @GuardedBy("sUidOwnerMap")
     private native int native_setUidRule(int childChain, int uid, int firewallRule);
-    @GuardedBy("sUidOwnerMap")
     private native int native_addUidInterfaceRules(String ifName, int[] uids);
-    @GuardedBy("sUidOwnerMap")
     private native int native_removeUidInterfaceRules(int[] uids);
-    @GuardedBy("sUidOwnerMap")
     private native int native_updateUidLockdownRule(int uid, boolean add);
     private native int native_swapActiveStatsMap();
     private native void native_setPermissionForUids(int permissions, int[] uids);
diff --git a/service/src/com/android/server/connectivity/DscpPolicyValue.java b/service/src/com/android/server/connectivity/DscpPolicyValue.java
index 4bb41da..fed96b4 100644
--- a/service/src/com/android/server/connectivity/DscpPolicyValue.java
+++ b/service/src/com/android/server/connectivity/DscpPolicyValue.java
@@ -43,10 +43,10 @@
     @Field(order = 3, type = Type.UBE16)
     public final int srcPort;
 
-    @Field(order = 4, type = Type.UBE16)
+    @Field(order = 4, type = Type.U16)
     public final int dstPortStart;
 
-    @Field(order = 5, type = Type.UBE16)
+    @Field(order = 5, type = Type.U16)
     public final int dstPortEnd;
 
     @Field(order = 6, type = Type.U8)
@@ -61,8 +61,7 @@
     private static final int SRC_IP_MASK = 0x1;
     private static final int DST_IP_MASK = 0x02;
     private static final int SRC_PORT_MASK = 0x4;
-    private static final int DST_PORT_MASK = 0x8;
-    private static final int PROTO_MASK = 0x10;
+    private static final int PROTO_MASK = 0x8;
 
     private boolean ipEmpty(final byte[] ip) {
         for (int i = 0; i < ip.length; i++) {
@@ -111,9 +110,6 @@
         if (srcPort != -1) {
             mask |=  SRC_PORT_MASK;
         }
-        if (dstPortStart != -1 && dstPortEnd != -1) {
-            mask |=  DST_PORT_MASK;
-        }
         if (proto != -1) {
             mask |=  PROTO_MASK;
         }
@@ -131,7 +127,7 @@
         // If they are -1 BpfMap write will throw errors.
         this.srcPort = srcPort != -1 ? srcPort : 0;
         this.dstPortStart = dstPortStart != -1 ? dstPortStart : 0;
-        this.dstPortEnd = dstPortEnd != -1 ? dstPortEnd : 0;
+        this.dstPortEnd = dstPortEnd != -1 ? dstPortEnd : 65535;
         this.proto = proto != -1 ? proto : 0;
 
         this.dscp = dscp;
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 0196f48..06f7300 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -429,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;
@@ -923,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
@@ -933,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.
@@ -969,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
@@ -1215,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;
-        }
     }
 
     /**
@@ -3567,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();
             }
@@ -3605,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
@@ -3635,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);
@@ -8548,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.
@@ -8581,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();
     }
 
@@ -9040,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>();
@@ -9073,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();
@@ -9088,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();
@@ -9127,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);
@@ -9147,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();
@@ -9161,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();
@@ -9175,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();
@@ -9189,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();
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/connectivity/NetworkNotificationManagerTest.java b/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
index 2cf5d8e..53097b6 100644
--- a/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
@@ -385,11 +385,13 @@
         doReturn(true).when(mResources).getBoolean(
                 R.bool.config_notifyNoInternetAsDialogWhenHighPriority);
 
+        final Instrumentation instr = InstrumentationRegistry.getInstrumentation();
+        UiDevice.getInstance(instr).pressHome();
+
         mManager.showNotification(TEST_NOTIF_ID, NETWORK_SWITCH, mWifiNai, mCellNai, null, false);
         // Non-"no internet" notifications are not affected
         verify(mNotificationManager).notify(eq(TEST_NOTIF_TAG), eq(NETWORK_SWITCH.eventId), any());
 
-        final Instrumentation instr = InstrumentationRegistry.getInstrumentation();
         final Context ctx = instr.getContext();
         final String testAction = "com.android.connectivity.coverage.TEST_DIALOG";
         final Intent intent = new Intent(testAction)
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index 6f25d1b..041e4ea 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -92,7 +92,6 @@
 import android.net.LinkProperties;
 import android.net.LocalSocket;
 import android.net.Network;
-import android.net.NetworkAgent;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo.DetailedState;
 import android.net.RouteInfo;
@@ -246,7 +245,7 @@
     @Mock private Vpn.SystemServices mSystemServices;
     @Mock private Vpn.IkeSessionWrapper mIkeSessionWrapper;
     @Mock private Vpn.Ikev2SessionCreator mIkev2SessionCreator;
-    @Mock private NetworkAgent mMockNetworkAgent;
+    @Mock private Vpn.VpnNetworkAgentWrapper mMockNetworkAgent;
     @Mock private ConnectivityManager mConnectivityManager;
     @Mock private IpSecService mIpSecService;
     @Mock private VpnProfileStore mVpnProfileStore;
@@ -870,7 +869,7 @@
     public void testRefreshPlatformVpnAppExclusionList_updatesExcludedUids() throws Exception {
         final Vpn vpn = prepareVpnForVerifyAppExclusionList();
         vpn.setAppExclusionList(TEST_VPN_PKG, Arrays.asList(PKGS));
-        verify(mMockNetworkAgent).sendNetworkCapabilities(any());
+        verify(mMockNetworkAgent).doSendNetworkCapabilities(any());
         assertEquals(Arrays.asList(PKGS), vpn.getAppExclusionList(TEST_VPN_PKG));
 
         reset(mMockNetworkAgent);
@@ -887,7 +886,7 @@
                 vpn.mNetworkCapabilities.getUids());
         ArgumentCaptor<NetworkCapabilities> ncCaptor =
                 ArgumentCaptor.forClass(NetworkCapabilities.class);
-        verify(mMockNetworkAgent).sendNetworkCapabilities(ncCaptor.capture());
+        verify(mMockNetworkAgent).doSendNetworkCapabilities(ncCaptor.capture());
         assertEquals(makeVpnUidRange(PRIMARY_USER.id, newExcludedUids),
                 ncCaptor.getValue().getUids());
 
@@ -902,7 +901,7 @@
         assertEquals(Arrays.asList(PKGS), vpn.getAppExclusionList(TEST_VPN_PKG));
         assertEquals(makeVpnUidRange(PRIMARY_USER.id, newExcludedUids),
                 vpn.mNetworkCapabilities.getUids());
-        verify(mMockNetworkAgent).sendNetworkCapabilities(ncCaptor.capture());
+        verify(mMockNetworkAgent).doSendNetworkCapabilities(ncCaptor.capture());
         assertEquals(makeVpnUidRange(PRIMARY_USER.id, newExcludedUids),
                 ncCaptor.getValue().getUids());
     }
@@ -1854,7 +1853,7 @@
                 Collections.singletonList(TEST_NETWORK_2),
                 vpnSnapShot.vpn.mNetworkCapabilities.getUnderlyingNetworks());
         verify(mMockNetworkAgent)
-                .setUnderlyingNetworks(Collections.singletonList(TEST_NETWORK_2));
+                .doSetUnderlyingNetworks(Collections.singletonList(TEST_NETWORK_2));
 
         vpnSnapShot.vpn.mVpnRunner.exitVpnRunner();
     }
@@ -1896,7 +1895,7 @@
                 Collections.singletonList(TEST_NETWORK_2),
                 vpnSnapShot.vpn.mNetworkCapabilities.getUnderlyingNetworks());
         verify(mMockNetworkAgent)
-                .setUnderlyingNetworks(Collections.singletonList(TEST_NETWORK_2));
+                .doSetUnderlyingNetworks(Collections.singletonList(TEST_NETWORK_2));
 
         vpnSnapShot.vpn.mVpnRunner.exitVpnRunner();
     }
@@ -1904,7 +1903,7 @@
     private void verifyHandlingNetworkLoss() throws Exception {
         final ArgumentCaptor<LinkProperties> lpCaptor =
                 ArgumentCaptor.forClass(LinkProperties.class);
-        verify(mMockNetworkAgent).sendLinkProperties(lpCaptor.capture());
+        verify(mMockNetworkAgent).doSendLinkProperties(lpCaptor.capture());
         final LinkProperties lp = lpCaptor.getValue();
 
         assertNull(lp.getInterfaceName());
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index 484d717..f64e35b 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.net;
 
+import static android.Manifest.permission.DUMP;
 import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
 import static android.Manifest.permission.UPDATE_DEVICE_STATS;
 import static android.app.usage.NetworkStatsManager.PREFIX_DEV;
@@ -156,7 +157,10 @@
 import org.mockito.MockitoAnnotations;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.time.Clock;
@@ -283,6 +287,7 @@
                 case PERMISSION_MAINLINE_NETWORK_STACK:
                 case READ_NETWORK_USAGE_HISTORY:
                 case UPDATE_DEVICE_STATS:
+                case DUMP:
                     return PERMISSION_GRANTED;
                 default:
                     return PERMISSION_DENIED;
@@ -857,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.
@@ -872,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.
@@ -892,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.
@@ -979,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.
@@ -994,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.
@@ -1010,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.
@@ -1024,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.
@@ -1135,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();
@@ -1169,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();
@@ -1681,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
@@ -1705,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
@@ -1727,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
@@ -2317,4 +2327,44 @@
         assertTrue(mAppUidStatsMap.containsKey(new UidStatsMapKey(UID_RED)));
         assertTrue(mUidCounterSetMap.containsKey(new U32(UID_RED)));
     }
+
+    private void assertDumpContains(final String dump, final String message) {
+        assertTrue(String.format("dump(%s) does not contain '%s'", dump, message),
+                dump.contains(message));
+    }
+
+    private String getDump() {
+        final StringWriter sw = new StringWriter();
+        mService.dump(new FileDescriptor(), new PrintWriter(sw), new String[]{});
+        return sw.toString();
+    }
+
+    @Test
+    public void testDumpCookieTagMap() throws ErrnoException {
+        initBpfMapsWithTagData(UID_BLUE);
+
+        final String dump = getDump();
+        assertDumpContains(dump, "mCookieTagMap: OK");
+        assertDumpContains(dump, "cookie=2002 tag=0x1 uid=1002");
+        assertDumpContains(dump, "cookie=3002 tag=0x2 uid=1002");
+    }
+
+    @Test
+    public void testDumpUidCounterSetMap() throws ErrnoException {
+        initBpfMapsWithTagData(UID_BLUE);
+
+        final String dump = getDump();
+        assertDumpContains(dump, "mUidCounterSetMap: OK");
+        assertDumpContains(dump, "uid=1002 set=1");
+    }
+
+    @Test
+    public void testAppUidStatsMap() throws ErrnoException {
+        initBpfMapsWithTagData(UID_BLUE);
+
+        final String dump = getDump();
+        assertDumpContains(dump, "mAppUidStatsMap: OK");
+        assertDumpContains(dump, "uid rxBytes rxPackets txBytes txPackets");
+        assertDumpContains(dump, "1002 10000 10 6000 6");
+    }
 }