Merge "TestOnly: reduce duplicated start/stop hotspot verification"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 4774866..1e8babf 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -36,6 +36,17 @@
     },
     // CTS tests that target older SDKs.
     {
+      "name": "CtsNetTestCasesMaxTargetSdk30",
+      "options": [
+        {
+          "exclude-annotation": "com.android.testutils.SkipPresubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.RequiresDevice"
+        }
+      ]
+    },
+    {
       "name": "CtsNetTestCasesMaxTargetSdk31",
       "options": [
         {
@@ -103,6 +114,17 @@
       ]
     },
     {
+      "name": "CtsNetTestCasesMaxTargetSdk30[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
+      "options": [
+        {
+          "exclude-annotation": "com.android.testutils.SkipPresubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.RequiresDevice"
+        }
+      ]
+    },
+    {
       "name": "CtsNetTestCasesMaxTargetSdk31[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
       "options": [
         {
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index a7028b7..60b4e91 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -108,7 +108,10 @@
 
     androidManifest: "AndroidManifest.xml",
 
-    compat_configs: ["connectivity-platform-compat-config"],
+    compat_configs: [
+        "connectivity-platform-compat-config",
+        "connectivity-t-platform-compat-config",
+    ],
 }
 
 apex_key {
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/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index ef4f052..06586e2 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -86,7 +86,6 @@
 import com.android.net.module.util.bpf.TetherStatsValue;
 import com.android.net.module.util.structs.Ipv6Header;
 import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 import com.android.testutils.DeviceInfoUtils;
 import com.android.testutils.DumpTestUtils;
@@ -144,6 +143,7 @@
     private static final int TX_UDP_PACKET_COUNT = 123;
     private static final long WAIT_RA_TIMEOUT_MS = 2000;
 
+    private static final MacAddress TEST_MAC = MacAddress.fromString("1:2:3:4:5:6");
     private static final LinkAddress TEST_IP4_ADDR = new LinkAddress("10.0.0.1/8");
     private static final LinkAddress TEST_IP6_ADDR = new LinkAddress("2001:db8:1::101/64");
     private static final InetAddress TEST_IP4_DNS = parseNumericAddress("8.8.8.8");
@@ -151,6 +151,8 @@
     private static final IpPrefix TEST_NAT64PREFIX = new IpPrefix("64:ff9b::/96");
     private static final Inet6Address REMOTE_NAT64_ADDR =
             (Inet6Address) parseNumericAddress("64:ff9b::808:808");
+    private static final Inet6Address REMOTE_IP6_ADDR =
+            (Inet6Address) parseNumericAddress("2002:db8:1::515:ca");
     private static final ByteBuffer TEST_REACHABILITY_PAYLOAD =
             ByteBuffer.wrap(new byte[] { (byte) 0x55, (byte) 0xaa });
 
@@ -795,8 +797,7 @@
     }
 
     private void runPing6Test(TetheringTester tester) throws Exception {
-        TetheredDevice tethered = tester.createTetheredDevice(MacAddress.fromString("1:2:3:4:5:6"),
-                true /* hasIpv6 */);
+        TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */);
         Inet6Address remoteIp6Addr = (Inet6Address) parseNumericAddress("2400:222:222::222");
         ByteBuffer request = Ipv6Utils.buildEchoRequestPacket(tethered.macAddr,
                 tethered.routerMacAddr, tethered.ipv6Addr, remoteIp6Addr);
@@ -836,12 +837,10 @@
     private static final short ID = 27149;
     private static final short FLAGS_AND_FRAGMENT_OFFSET = (short) 0x4000; // flags=DF, offset=0
     private static final byte TIME_TO_LIVE = (byte) 0x40;
-    private static final ByteBuffer PAYLOAD =
+    private static final ByteBuffer RX_PAYLOAD =
             ByteBuffer.wrap(new byte[] { (byte) 0x12, (byte) 0x34 });
-    private static final ByteBuffer PAYLOAD2 =
+    private static final ByteBuffer TX_PAYLOAD =
             ByteBuffer.wrap(new byte[] { (byte) 0x56, (byte) 0x78 });
-    private static final ByteBuffer PAYLOAD3 =
-            ByteBuffer.wrap(new byte[] { (byte) 0x9a, (byte) 0xbc });
 
     @NonNull
     private ByteBuffer buildUdpPacket(
@@ -870,7 +869,7 @@
         final PacketBuilder packetBuilder = new PacketBuilder(buffer);
 
         // [1] Ethernet header
-        if (hasEther) packetBuilder.writeL2Header(srcMac, dstMac, (short) ETHER_TYPE_IPV4);
+        if (hasEther) packetBuilder.writeL2Header(srcMac, dstMac, (short) ethType);
 
         // [2] IP header
         if (ipProto == IPPROTO_IP) {
@@ -903,6 +902,68 @@
                 dstPort, payload);
     }
 
+    private boolean isAddressIpv4(@NonNull final  InetAddress srcIp,
+            @NonNull final InetAddress dstIp) {
+        if (srcIp instanceof Inet4Address && dstIp instanceof Inet4Address) return true;
+        if (srcIp instanceof Inet6Address && dstIp instanceof Inet6Address) return false;
+
+        fail("Unsupported conditions: srcIp " + srcIp + ", dstIp " + dstIp);
+        return false;  // unreachable
+    }
+
+    private void sendDownloadPacketUdp(@NonNull final InetAddress srcIp,
+            @NonNull final InetAddress dstIp, @NonNull final TetheringTester tester,
+            boolean is6To4) throws Exception {
+        if (is6To4) {
+            assertFalse("CLAT download test must sends IPv6 packet", isAddressIpv4(srcIp, dstIp));
+        }
+
+        // Expected received UDP packet IP protocol. While testing CLAT (is6To4 = true), the packet
+        // on downstream must be IPv4. Otherwise, the IP protocol of test packet is the same on
+        // both downstream and upstream.
+        final boolean isIpv4 = is6To4 ? true : isAddressIpv4(srcIp, dstIp);
+
+        final ByteBuffer testPacket = buildUdpPacket(srcIp, dstIp, REMOTE_PORT /* srcPort */,
+                LOCAL_PORT /* dstPort */, RX_PAYLOAD);
+        tester.verifyDownload(testPacket, p -> {
+            Log.d(TAG, "Packet in downstream: " + dumpHexString(p));
+            return isExpectedUdpPacket(p, true /* hasEther */, isIpv4, RX_PAYLOAD);
+        });
+    }
+
+    private void sendUploadPacketUdp(@NonNull final MacAddress srcMac,
+            @NonNull final MacAddress dstMac, @NonNull final InetAddress srcIp,
+            @NonNull final InetAddress dstIp, @NonNull final TetheringTester tester,
+            boolean is4To6) throws Exception {
+        if (is4To6) {
+            assertTrue("CLAT upload test must sends IPv4 packet", isAddressIpv4(srcIp, dstIp));
+        }
+
+        // Expected received UDP packet IP protocol. While testing CLAT (is4To6 = true), the packet
+        // on upstream must be IPv6. Otherwise, the IP protocol of test packet is the same on
+        // both downstream and upstream.
+        final boolean isIpv4 = is4To6 ? false : isAddressIpv4(srcIp, dstIp);
+
+        final ByteBuffer testPacket = buildUdpPacket(srcMac, dstMac, srcIp, dstIp,
+                LOCAL_PORT /* srcPort */, REMOTE_PORT /* dstPort */, TX_PAYLOAD);
+        tester.verifyUpload(testPacket, p -> {
+            Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
+            return isExpectedUdpPacket(p, false /* hasEther */, isIpv4, TX_PAYLOAD);
+        });
+    }
+
+    @Test
+    public void testTetherUdpV6() throws Exception {
+        final TetheringTester tester = initTetheringTester(toList(TEST_IP6_ADDR),
+                toList(TEST_IP6_DNS));
+        final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */);
+        sendUploadPacketUdp(tethered.macAddr, tethered.routerMacAddr,
+                tethered.ipv6Addr, REMOTE_IP6_ADDR, tester, false /* is4To6 */);
+        sendDownloadPacketUdp(REMOTE_IP6_ADDR, tethered.ipv6Addr, tester, false /* is6To4 */);
+
+        // TODO: test BPF offload maps {rule, stats}.
+    }
+
     // TODO: remove ipv4 verification (is4To6 = false) once upstream connected notification race is
     // fixed. See #runUdp4Test.
     //
@@ -934,9 +995,10 @@
         return null;
     }
 
-    private void runUdp4Test(TetheringTester tester, boolean usingBpf) throws Exception {
-        final TetheredDevice tethered = tester.createTetheredDevice(MacAddress.fromString(
-                "1:2:3:4:5:6"), false /* hasIpv6 */);
+    private void runUdp4Test(boolean verifyBpf) throws Exception {
+        final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR),
+                toList(TEST_IP4_DNS));
+        final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */);
 
         // TODO: remove the connectivity verification for upstream connected notification race.
         // Because async upstream connected notification can't guarantee the tethering routing is
@@ -946,27 +1008,15 @@
         // refactors upstream connected notification from async to sync.
         probeV4TetheringConnectivity(tester, tethered, false /* is4To6 */);
 
-        // Send a UDP packet in original direction.
-        final ByteBuffer originalPacket = buildUdpPacket(tethered.macAddr,
-                tethered.routerMacAddr, tethered.ipv4Addr /* srcIp */,
-                REMOTE_IP4_ADDR /* dstIp */, LOCAL_PORT /* srcPort */, REMOTE_PORT /* dstPort */,
-                PAYLOAD /* payload */);
-        tester.verifyUpload(originalPacket, p -> {
-            Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
-            return isExpectedUdpPacket(p, false /* hasEther */, true /* isIpv4 */, PAYLOAD);
-        });
+        final MacAddress srcMac = tethered.macAddr;
+        final MacAddress dstMac = tethered.routerMacAddr;
+        final InetAddress remoteIp = REMOTE_IP4_ADDR;
+        final InetAddress tetheringUpstreamIp = TEST_IP4_ADDR.getAddress();
+        final InetAddress clientIp = tethered.ipv4Addr;
+        sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester, false /* is4To6 */);
+        sendDownloadPacketUdp(remoteIp, tetheringUpstreamIp, tester, false /* is6To4 */);
 
-        // Send a UDP packet in reply direction.
-        final Inet4Address publicIp4Addr = (Inet4Address) TEST_IP4_ADDR.getAddress();
-        final ByteBuffer replyPacket = buildUdpPacket(REMOTE_IP4_ADDR /* srcIp */,
-                publicIp4Addr /* dstIp */, REMOTE_PORT /* srcPort */, LOCAL_PORT /* dstPort */,
-                PAYLOAD2 /* payload */);
-        tester.verifyDownload(replyPacket, p -> {
-            Log.d(TAG, "Packet in downstream: " + dumpHexString(p));
-            return isExpectedUdpPacket(p, true /* hasEther */, true /* isIpv4 */, PAYLOAD2);
-        });
-
-        if (usingBpf) {
+        if (verifyBpf) {
             // Send second UDP packet in original direction.
             // The BPF coordinator only offloads the ASSURED conntrack entry. The "request + reply"
             // packets can make status IPS_SEEN_REPLY to be set. Need one more packet to make
@@ -976,14 +1026,7 @@
             // See kernel upstream commit b7b1d02fc43925a4d569ec221715db2dfa1ce4f5 and
             // nf_conntrack_udp_packet in net/netfilter/nf_conntrack_proto_udp.c
             Thread.sleep(UDP_STREAM_TS_MS);
-            final ByteBuffer originalPacket2 = buildUdpPacket(tethered.macAddr,
-                    tethered.routerMacAddr, tethered.ipv4Addr /* srcIp */,
-                    REMOTE_IP4_ADDR /* dstIp */, LOCAL_PORT /* srcPort */,
-                    REMOTE_PORT /* dstPort */, PAYLOAD3 /* payload */);
-            tester.verifyUpload(originalPacket2, p -> {
-                Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
-                return isExpectedUdpPacket(p, false /* hasEther */, true /* isIpv4 */, PAYLOAD3);
-            });
+            sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester, false /* is4To6 */);
 
             // [1] Verify IPv4 upstream rule map.
             final HashMap<Tether4Key, Tether4Value> upstreamMap = pollRawMapFromDump(
@@ -1002,7 +1045,7 @@
             assertEquals(REMOTE_PORT, upstream4Key.dstPort);
 
             final Tether4Value upstream4Value = rule.getValue();
-            assertTrue(Arrays.equals(publicIp4Addr.getAddress(),
+            assertTrue(Arrays.equals(tetheringUpstreamIp.getAddress(),
                     InetAddress.getByAddress(upstream4Value.src46).getAddress()));
             assertEquals(LOCAL_PORT, upstream4Value.srcPort);
             assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(),
@@ -1016,18 +1059,13 @@
 
             // Send packets on original direction.
             for (int i = 0; i < TX_UDP_PACKET_COUNT; i++) {
-                tester.verifyUpload(originalPacket, p -> {
-                    Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
-                    return isExpectedUdpPacket(p, false /* hasEther */, true /* isIpv4 */, PAYLOAD);
-                });
+                sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester,
+                        false /* is4To6 */);
             }
 
             // Send packets on reply direction.
             for (int i = 0; i < RX_UDP_PACKET_COUNT; i++) {
-                tester.verifyDownload(replyPacket, p -> {
-                    Log.d(TAG, "Packet in downstream: " + dumpHexString(p));
-                    return isExpectedUdpPacket(p, true /* hasEther */, true /* isIpv4 */, PAYLOAD2);
-                });
+                sendDownloadPacketUdp(remoteIp, tetheringUpstreamIp, tester, false /* is6To4 */);
             }
 
             // Dump stats map to verify.
@@ -1086,13 +1124,6 @@
         return new TetheringTester(mDownstreamReader, mUpstreamReader);
     }
 
-    @Test
-    @IgnoreAfter(Build.VERSION_CODES.R)
-    public void testTetherUdpV4UpToR() throws Exception {
-        runUdp4Test(initTetheringTester(toList(TEST_IP4_ADDR), toList(TEST_IP4_DNS)),
-                false /* usingBpf */);
-    }
-
     private static boolean isUdpOffloadSupportedByKernel(final String kernelVersion) {
         final KVersion current = DeviceInfoUtils.getMajorMinorSubminorVersion(kernelVersion);
         return current.isInRange(new KVersion(4, 14, 222), new KVersion(4, 19, 0))
@@ -1119,24 +1150,47 @@
         assertTrue(isUdpOffloadSupportedByKernel("5.10.0"));
     }
 
-    // TODO: refactor test testTetherUdpV4* into IPv4 UDP non-offload and offload tests.
-    // That can be easier to know which feature is verified from test results.
+    private static void assumeKernelSupportBpfOffloadUdpV4() {
+        final String kernelVersion = VintfRuntimeInfo.getKernelVersion();
+        assumeTrue("Kernel version " + kernelVersion + " doesn't support IPv4 UDP BPF offload",
+                isUdpOffloadSupportedByKernel(kernelVersion));
+    }
+
+    @Test
+    public void testKernelSupportBpfOffloadUdpV4() throws Exception {
+        assumeKernelSupportBpfOffloadUdpV4();
+    }
+
+    @Test
+    public void testTetherConfigBpfOffloadEnabled() throws Exception {
+        assumeTrue(isTetherConfigBpfOffloadEnabled());
+    }
+
+    /**
+     * Basic IPv4 UDP tethering test. Verify that UDP tethered packets are transferred no matter
+     * using which data path.
+     */
+    @Test
+    public void testTetherUdpV4() throws Exception {
+        runUdp4Test(false /* verifyBpf */);
+    }
+
+    /**
+     * BPF offload IPv4 UDP tethering test. Verify that UDP tethered packets are offloaded by BPF.
+     * Minimum test requirement:
+     * 1. S+ device.
+     * 2. Tethering config enables tethering BPF offload.
+     * 3. Kernel supports IPv4 UDP BPF offload. See #isUdpOffloadSupportedByKernel.
+     *
+     * TODO: consider enabling the test even tethering config disables BPF offload. See b/238288883
+     */
     @Test
     @IgnoreUpTo(Build.VERSION_CODES.R)
-    public void testTetherUdpV4AfterR() throws Exception {
-        final String kernelVersion = VintfRuntimeInfo.getKernelVersion();
-        final boolean isUdpOffloadSupported = isUdpOffloadSupportedByKernel(kernelVersion);
-        if (!isUdpOffloadSupported) {
-            Log.i(TAG, "testTetherUdpV4AfterR will skip BPF offload test for kernel "
-                    + kernelVersion);
-        }
-        final boolean isTetherConfigBpfOffloadEnabled = isTetherConfigBpfOffloadEnabled();
-        if (!isTetherConfigBpfOffloadEnabled) {
-            Log.i(TAG, "testTetherUdpV4AfterR will skip BPF offload test "
-                    + "because tethering config doesn't enable BPF offload.");
-        }
-        runUdp4Test(initTetheringTester(toList(TEST_IP4_ADDR), toList(TEST_IP4_DNS)),
-                isUdpOffloadSupported && isTetherConfigBpfOffloadEnabled);
+    public void testTetherUdpV4_VerifyBpf() throws Exception {
+        assumeTrue("Tethering config disabled BPF offload", isTetherConfigBpfOffloadEnabled());
+        assumeKernelSupportBpfOffloadUdpV4();
+
+        runUdp4Test(true /* verifyBpf */);
     }
 
     @Nullable
@@ -1236,33 +1290,23 @@
     // sending out an IPv4 packet and extracting the source address from CLAT translated IPv6
     // packet.
     //
-    private void runClatUdpTest(TetheringTester tester) throws Exception {
-        final TetheredDevice tethered = tester.createTetheredDevice(MacAddress.fromString(
-                "1:2:3:4:5:6"), true /* hasIpv6 */);
+    private void runClatUdpTest() throws Exception {
+        // CLAT only starts on IPv6 only network.
+        final TetheringTester tester = initTetheringTester(toList(TEST_IP6_ADDR),
+                toList(TEST_IP6_DNS));
+        final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */);
 
         // Get CLAT IPv6 address.
-        final Inet6Address clatAddr6 = getClatIpv6Address(tester, tethered);
+        final Inet6Address clatIp6 = getClatIpv6Address(tester, tethered);
 
         // Send an IPv4 UDP packet in original direction.
         // IPv4 packet -- CLAT translation --> IPv6 packet
-        final ByteBuffer originalPacket = buildUdpPacket(tethered.macAddr,
-                tethered.routerMacAddr, tethered.ipv4Addr /* srcIp */,
-                REMOTE_IP4_ADDR /* dstIp */, LOCAL_PORT /* srcPort */, REMOTE_PORT /* dstPort */,
-                PAYLOAD /* payload */);
-        tester.verifyUpload(originalPacket, p -> {
-            Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
-            return isExpectedUdpPacket(p, false /* hasEther */, false /* isIpv4 */, PAYLOAD);
-        });
+        sendUploadPacketUdp(tethered.macAddr, tethered.routerMacAddr, tethered.ipv4Addr,
+                REMOTE_IP4_ADDR, tester, true /* is4To6 */);
 
         // Send an IPv6 UDP packet in reply direction.
         // IPv6 packet -- CLAT translation --> IPv4 packet
-        final ByteBuffer replyPacket = buildUdpPacket(REMOTE_NAT64_ADDR /* srcIp */,
-                clatAddr6 /* dstIp */, REMOTE_PORT /* srcPort */, LOCAL_PORT /* dstPort */,
-                PAYLOAD2 /* payload */);
-        tester.verifyDownload(replyPacket, p -> {
-            Log.d(TAG, "Packet in downstream: " + dumpHexString(p));
-            return isExpectedUdpPacket(p, true /* hasEther */, true /* isIpv4 */, PAYLOAD2);
-        });
+        sendDownloadPacketUdp(REMOTE_NAT64_ADDR, clatIp6, tester, true /* is6To4 */);
 
         // TODO: test CLAT bpf maps.
     }
@@ -1270,8 +1314,7 @@
     @Test
     @IgnoreUpTo(Build.VERSION_CODES.R)
     public void testTetherClatUdp() throws Exception {
-        // CLAT only starts on IPv6 only network.
-        runClatUdpTest(initTetheringTester(toList(TEST_IP6_ADDR), toList(TEST_IP6_DNS)));
+        runClatUdpTest();
     }
 
     private <T> List<T> toList(T... array) {
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/Android.bp b/bpf_progs/Android.bp
index c2e28f4..8eb9cfd 100644
--- a/bpf_progs/Android.bp
+++ b/bpf_progs/Android.bp
@@ -50,7 +50,8 @@
         "//packages/modules/Connectivity/service/native/libs/libclat",
         "//packages/modules/Connectivity/Tethering",
         "//packages/modules/Connectivity/service/native",
-        "//packages/modules/Connectivity/tests/native",
+        "//packages/modules/Connectivity/tests/native/connectivity_native_test",
+        "//packages/modules/Connectivity/tests/native/utilities",
         "//packages/modules/Connectivity/service-t/native/libs/libnetworkstats",
         "//packages/modules/Connectivity/tests/unit/jni",
         "//system/netd/tests",
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..3e4456f 100644
--- a/bpf_progs/dscpPolicy.c
+++ b/bpf_progs/dscpPolicy.c
@@ -57,7 +57,7 @@
     uint64_t cookie = bpf_get_socket_cookie(skb);
     if (!cookie) return;
 
-    uint16_t sport = 0;
+    __be16 sport = 0;
     uint16_t dport = 0;
     uint8_t protocol = 0;  // TODO: Use are reserved value? Or int (-1) and cast to uint below?
     struct in6_addr src_ip = {};
@@ -106,14 +106,14 @@
             udp = data + hdr_size;
             if ((void*)(udp + 1) > data_end) return;
             sport = udp->source;
-            dport = udp->dest;
+            dport = ntohs(udp->dest);
         } break;
         case IPPROTO_TCP: {
             struct tcphdr* tcp;
             tcp = data + hdr_size;
             if ((void*)(tcp + 1) > data_end) return;
             sport = tcp->source;
-            dport = tcp->dest;
+            dport = ntohs(tcp->dest);
         } break;
         default:
             return;
@@ -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 (dport < policy->dst_port_start) continue;
+        if (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..e565966 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;
@@ -59,11 +65,11 @@
 typedef struct {
     struct in6_addr src_ip;
     struct in6_addr dst_ip;
-    __u32 ifindex;
+    uint32_t ifindex;
     __be16 src_port;
-    __be16 dst_port;
-    __u8 proto;
-    __s8 dscp_val;  // -1 none, or 0..63 DSCP value
-    __u8 pad[2];
+    uint16_t dst_port;
+    uint8_t proto;
+    int8_t dscp_val;  // -1 none, or 0..63 DSCP value
+    uint8_t pad[2];
 } RuleEntry;
 STRUCT_SIZE(RuleEntry, 2 * 16 + 1 * 4 + 2 * 2 + 2 * 1 + 2);  // 44
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 80477f1..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",
@@ -142,3 +144,8 @@
         "//packages/modules/Wifi/service/tests/wifitests",
     ],
 }
+
+platform_compat_config {
+    name: "connectivity-t-platform-compat-config",
+    src: ":framework-connectivity-t",
+}
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-t/src/android/net/nsd/NsdManager.java b/framework-t/src/android/net/nsd/NsdManager.java
index 3fcc11b..fb3b1d6 100644
--- a/framework-t/src/android/net/nsd/NsdManager.java
+++ b/framework-t/src/android/net/nsd/NsdManager.java
@@ -139,17 +139,21 @@
      * The platform will only keep the daemon running as long as there are
      * any legacy apps connected.
      *
-     * After Android 12, directly communicate with native daemon might not
-     * work since the native damon won't always stay alive.
-     * Use the NSD APIs from NsdManager as the replacement is recommended.
-     * An another alternative could be bundling your own mdns solutions instead of
+     * After Android 12, direct communication with the native daemon might not work since the native
+     * daemon won't always stay alive. Using the NSD APIs from NsdManager as the replacement is
+     * recommended.
+     * Another alternative could be bundling your own mdns solutions instead of
      * depending on the system mdns native daemon.
      *
+     * This compatibility change applies to Android 13 and later only. To toggle behavior on
+     * Android 12 and Android 12L, use RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS.
+     *
      * @hide
      */
     @ChangeId
     @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S)
-    public static final long RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS = 191844585L;
+    // This was a platform change ID with value 191844585L before T
+    public static final long RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER = 235355681L;
 
     /**
      * Broadcast intent action to indicate whether network service discovery is
@@ -500,7 +504,7 @@
 
         // Only proactively start the daemon if the target SDK < S, otherwise the internal service
         // would automatically start/stop the native daemon as needed.
-        if (!CompatChanges.isChangeEnabled(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)) {
+        if (!CompatChanges.isChangeEnabled(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)) {
             try {
                 mService.startDaemon();
             } catch (RemoteException e) {
diff --git a/framework/jni/android_net_NetworkUtils.cpp b/framework/jni/android_net_NetworkUtils.cpp
index 857ece5..38e0059 100644
--- a/framework/jni/android_net_NetworkUtils.cpp
+++ b/framework/jni/android_net_NetworkUtils.cpp
@@ -53,7 +53,7 @@
     return static_cast<T>(res);
 }
 
-static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
+static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jclass clazz, jobject javaFd)
 {
     struct sock_filter filter_code[] = {
         // Reject all.
@@ -71,7 +71,7 @@
     }
 }
 
-static void android_net_utils_detachBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
+static void android_net_utils_detachBPFFilter(JNIEnv *env, jclass clazz, jobject javaFd)
 {
     int optval_ignored = 0;
     int fd = AFileDescriptor_getFd(env, javaFd);
@@ -82,13 +82,13 @@
     }
 }
 
-static jboolean android_net_utils_bindProcessToNetworkHandle(JNIEnv *env, jobject thiz,
+static jboolean android_net_utils_bindProcessToNetworkHandle(JNIEnv *env, jclass clazz,
         jlong netHandle)
 {
     return (jboolean) !android_setprocnetwork(netHandle);
 }
 
-static jlong android_net_utils_getBoundNetworkHandleForProcess(JNIEnv *env, jobject thiz)
+static jlong android_net_utils_getBoundNetworkHandleForProcess(JNIEnv *env, jclass clazz)
 {
     net_handle_t network;
     if (android_getprocnetwork(&network) != 0) {
@@ -99,13 +99,13 @@
     return (jlong) network;
 }
 
-static jboolean android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz,
+static jboolean android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv *env, jclass clazz,
         jint netId, jlong netHandle)
 {
     return (jboolean) !android_setprocdns(netHandle);
 }
 
-static jint android_net_utils_bindSocketToNetworkHandle(JNIEnv *env, jobject thiz, jobject javaFd,
+static jint android_net_utils_bindSocketToNetworkHandle(JNIEnv *env, jclass clazz, jobject javaFd,
                                                   jlong netHandle) {
     return android_setsocknetwork(netHandle, AFileDescriptor_getFd(env, javaFd));
 }
@@ -119,7 +119,7 @@
     return true;
 }
 
-static jobject android_net_utils_resNetworkQuery(JNIEnv *env, jobject thiz, jlong netHandle,
+static jobject android_net_utils_resNetworkQuery(JNIEnv *env, jclass clazz, jlong netHandle,
         jstring dname, jint ns_class, jint ns_type, jint flags) {
     const jsize javaCharsCount = env->GetStringLength(dname);
     const jsize byteCountUTF8 = env->GetStringUTFLength(dname);
@@ -140,7 +140,7 @@
     return jniCreateFileDescriptor(env, fd);
 }
 
-static jobject android_net_utils_resNetworkSend(JNIEnv *env, jobject thiz, jlong netHandle,
+static jobject android_net_utils_resNetworkSend(JNIEnv *env, jclass clazz, jlong netHandle,
         jbyteArray msg, jint msgLen, jint flags) {
     uint8_t data[MAXCMDSIZE];
 
@@ -155,7 +155,7 @@
     return jniCreateFileDescriptor(env, fd);
 }
 
-static jobject android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, jobject javaFd) {
+static jobject android_net_utils_resNetworkResult(JNIEnv *env, jclass clazz, jobject javaFd) {
     int fd = AFileDescriptor_getFd(env, javaFd);
     int rcode;
     uint8_t buf[MAXPACKETSIZE] = {0};
@@ -181,13 +181,13 @@
     return env->NewObject(class_DnsResponse, ctor, answer, rcode);
 }
 
-static void android_net_utils_resNetworkCancel(JNIEnv *env, jobject thiz, jobject javaFd) {
+static void android_net_utils_resNetworkCancel(JNIEnv *env, jclass clazz, jobject javaFd) {
     int fd = AFileDescriptor_getFd(env, javaFd);
     android_res_cancel(fd);
     jniSetFileDescriptorOfFD(env, javaFd, -1);
 }
 
-static jobject android_net_utils_getDnsNetwork(JNIEnv *env, jobject thiz) {
+static jobject android_net_utils_getDnsNetwork(JNIEnv *env, jclass clazz) {
     net_handle_t dnsNetHandle = NETWORK_UNSPECIFIED;
     if (int res = android_getprocdns(&dnsNetHandle) < 0) {
         jniThrowErrnoException(env, "getDnsNetwork", -res);
@@ -204,7 +204,7 @@
             static_cast<jlong>(dnsNetHandle));
 }
 
-static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) {
+static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jclass clazz, jobject javaFd) {
     if (javaFd == NULL) {
         jniThrowNullPointerException(env, NULL);
         return NULL;
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/framework/src/android/net/NetworkScore.java b/framework/src/android/net/NetworkScore.java
index 7be7deb..815e2b0 100644
--- a/framework/src/android/net/NetworkScore.java
+++ b/framework/src/android/net/NetworkScore.java
@@ -181,7 +181,7 @@
 
     @Override
     public String toString() {
-        return "Score(" + mLegacyInt + " ; Policies : " + mPolicies + ")";
+        return "Score(Policies : " + mPolicies + ")";
     }
 
     @Override
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/ethernet/EthernetConfigStore.java b/service-t/src/com/android/server/ethernet/EthernetConfigStore.java
index 6006539..e4445d0 100644
--- a/service-t/src/com/android/server/ethernet/EthernetConfigStore.java
+++ b/service-t/src/com/android/server/ethernet/EthernetConfigStore.java
@@ -107,8 +107,13 @@
     }
 
     private void loadConfigFileLocked(final String filepath) {
+        // readIpConfigurations can return null when the version is invalid.
         final ArrayMap<String, IpConfiguration> configs =
                 IpConfigStore.readIpConfigurations(filepath);
+        if (configs == null) {
+            Log.e(TAG, "IpConfigStore#readIpConfigurations() returned null");
+            return;
+        }
         mIpConfigurations.putAll(configs);
     }
 
diff --git a/service-t/src/com/android/server/ethernet/EthernetTracker.java b/service-t/src/com/android/server/ethernet/EthernetTracker.java
index 3e71093..be9beed 100644
--- a/service-t/src/com/android/server/ethernet/EthernetTracker.java
+++ b/service-t/src/com/android/server/ethernet/EthernetTracker.java
@@ -87,14 +87,17 @@
     private static final String TEST_IFACE_REGEXP = TEST_TAP_PREFIX + "\\d+";
 
     /**
-     * Interface names we track. This is a product-dependent regular expression, plus,
-     * if setIncludeTestInterfaces is true, any test interfaces.
+     * Interface names we track. This is a product-dependent regular expression.
+     * Use isValidEthernetInterface to check if a interface name is a valid ethernet interface (this
+     * includes test interfaces if setIncludeTestInterfaces is set to true).
      */
-    private volatile String mIfaceMatch;
+    private final String mIfaceMatch;
+
     /**
      * Track test interfaces if true, don't track otherwise.
+     * Volatile is needed as getInterfaceList() does not run on the handler thread.
      */
-    private boolean mIncludeTestInterfaces = false;
+    private volatile boolean mIncludeTestInterfaces = false;
 
     /** Mapping between {iface name | mac address} -> {NetworkCapabilities} */
     private final ConcurrentHashMap<String, NetworkCapabilities> mNetworkCapabilities =
@@ -161,7 +164,7 @@
         mDeps = deps;
 
         // Interface match regex.
-        updateIfaceMatchRegexp();
+        mIfaceMatch = mDeps.getInterfaceRegexFromResource(mContext);
 
         // Read default Ethernet interface configuration from resources
         final String[] interfaceConfigs = mDeps.getInterfaceConfigFromResource(context);
@@ -320,9 +323,17 @@
             Log.e(TAG, "Could not get list of interfaces " + e);
             return interfaceList;
         }
-        final String ifaceMatch = mIfaceMatch;
+
+        // There is a possible race with setIncludeTestInterfaces() which can affect
+        // isValidEthernetInterface (it returns true for test interfaces if setIncludeTestInterfaces
+        // is set to true).
+        // setIncludeTestInterfaces() is only used in tests, and since getInterfaceList() does not
+        // run on the handler thread, the behavior around setIncludeTestInterfaces() is
+        // indeterminate either way. This can easily be circumvented by waiting on a callback from
+        // a test interface after calling setIncludeTestInterfaces() before calling this function.
+        // In production code, this has no effect.
         for (String iface : ifaces) {
-            if (iface.matches(ifaceMatch)) interfaceList.add(iface);
+            if (isValidEthernetInterface(iface)) interfaceList.add(iface);
         }
         return interfaceList;
     }
@@ -357,7 +368,6 @@
     public void setIncludeTestInterfaces(boolean include) {
         mHandler.post(() -> {
             mIncludeTestInterfaces = include;
-            updateIfaceMatchRegexp();
             if (!include) {
                 removeTestData();
             }
@@ -569,7 +579,7 @@
     }
 
     private void maybeTrackInterface(String iface) {
-        if (!iface.matches(mIfaceMatch)) {
+        if (!isValidEthernetInterface(iface)) {
             return;
         }
 
@@ -840,12 +850,8 @@
         return ret;
     }
 
-    private void updateIfaceMatchRegexp() {
-        final String match = mDeps.getInterfaceRegexFromResource(mContext);
-        mIfaceMatch = mIncludeTestInterfaces
-                ? "(" + match + "|" + TEST_IFACE_REGEXP + ")"
-                : match;
-        Log.d(TAG, "Interface match regexp set to '" + mIfaceMatch + "'");
+    private boolean isValidEthernetInterface(String iface) {
+        return iface.matches(mIfaceMatch) || isValidTestInterface(iface);
     }
 
     /**
diff --git a/service-t/src/com/android/server/net/NetworkStatsFactory.java b/service-t/src/com/android/server/net/NetworkStatsFactory.java
index b628251..4a6741c 100644
--- a/service-t/src/com/android/server/net/NetworkStatsFactory.java
+++ b/service-t/src/com/android/server/net/NetworkStatsFactory.java
@@ -17,9 +17,7 @@
 package com.android.server.net;
 
 import static android.net.NetworkStats.INTERFACES_ALL;
-import static android.net.NetworkStats.SET_ALL;
 import static android.net.NetworkStats.TAG_ALL;
-import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
 
 import android.annotation.NonNull;
@@ -28,19 +26,12 @@
 import android.net.NetworkStats;
 import android.net.UnderlyingNetworkInfo;
 import android.os.ServiceSpecificException;
-import android.os.StrictMode;
 import android.os.SystemClock;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ProcFileReader;
-import com.android.net.module.util.CollectionUtils;
 import com.android.server.BpfNetMaps;
 
-import libcore.io.IoUtils;
-
-import java.io.File;
-import java.io.FileInputStream;
 import java.io.IOException;
 import java.net.ProtocolException;
 import java.util.Arrays;
@@ -61,18 +52,6 @@
 
     private static final String TAG = "NetworkStatsFactory";
 
-    private static final boolean USE_NATIVE_PARSING = true;
-    private static final boolean VALIDATE_NATIVE_STATS = false;
-
-    /** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */
-    private final File mStatsXtIfaceAll;
-    /** Path to {@code /proc/net/xt_qtaguid/iface_stat_fmt}. */
-    private final File mStatsXtIfaceFmt;
-    /** Path to {@code /proc/net/xt_qtaguid/stats}. */
-    private final File mStatsXtUid;
-
-    private final boolean mUseBpfStats;
-
     private final Context mContext;
 
     private final BpfNetMaps mBpfNetMaps;
@@ -96,6 +75,49 @@
     @GuardedBy("mPersistentDataLock")
     private NetworkStats mTunAnd464xlatAdjustedStats;
 
+    private final Dependencies mDeps;
+    /**
+     * Dependencies of NetworkStatsFactory, for injection in tests.
+     */
+    @VisibleForTesting
+    public static class Dependencies {
+        /**
+         * Parse detailed statistics from bpf into given {@link NetworkStats} object. Values
+         * are expected to monotonically increase since device boot.
+         */
+        @NonNull
+        public NetworkStats getNetworkStatsDetail(int limitUid, @Nullable String[] limitIfaces,
+                int limitTag) throws IOException {
+            final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
+            // TODO: remove both path and useBpfStats arguments.
+            // The path is never used if useBpfStats is true.
+            final int ret = nativeReadNetworkStatsDetail(stats, null /* path */,
+                    limitUid, limitIfaces, limitTag, true /* useBpfStats */);
+            if (ret != 0) {
+                throw new IOException("Failed to parse network stats");
+            }
+            return stats;
+        }
+        /**
+         * Parse device summary statistics from bpf into given {@link NetworkStats} object. Values
+         * are expected to monotonically increase since device boot.
+         */
+        @NonNull
+        public NetworkStats getNetworkStatsDev() throws IOException {
+            final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
+            final int ret = nativeReadNetworkStatsDev(stats);
+            if (ret != 0) {
+                throw new IOException("Failed to parse bpf iface stats");
+            }
+            return stats;
+        }
+
+        /** Create a new {@link BpfNetMaps}. */
+        public BpfNetMaps createBpfNetMaps(@NonNull Context ctx) {
+            return new BpfNetMaps(ctx);
+        }
+    }
+
     /**
      * (Stacked interface) -> (base interface) association for all connected ifaces since boot.
      *
@@ -164,30 +186,18 @@
     }
 
     public NetworkStatsFactory(@NonNull Context ctx) {
-        this(ctx, new File("/proc/"), true, new BpfNetMaps());
+        this(ctx, new Dependencies());
     }
 
     @VisibleForTesting
-    public NetworkStatsFactory(@NonNull Context ctx, File procRoot, boolean useBpfStats,
-            BpfNetMaps bpfNetMaps) {
-        mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all");
-        mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
-        mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
-        mUseBpfStats = useBpfStats;
-        mBpfNetMaps = bpfNetMaps;
+    public NetworkStatsFactory(@NonNull Context ctx, Dependencies deps) {
+        mBpfNetMaps = deps.createBpfNetMaps(ctx);
         synchronized (mPersistentDataLock) {
             mPersistSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), -1);
             mTunAnd464xlatAdjustedStats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
         }
         mContext = ctx;
-    }
-
-    public NetworkStats readBpfNetworkStatsDev() throws IOException {
-        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
-        if (nativeReadNetworkStatsDev(stats) != 0) {
-            throw new IOException("Failed to parse bpf iface stats");
-        }
-        return stats;
+        mDeps = deps;
     }
 
     /**
@@ -195,106 +205,18 @@
      * using {@code /proc/net/dev} style hooks, which may include non IP layer
      * traffic. Values monotonically increase since device boot, and may include
      * details about inactive interfaces.
-     *
-     * @throws IllegalStateException when problem parsing stats.
      */
     public NetworkStats readNetworkStatsSummaryDev() throws IOException {
-
-        // Return xt_bpf stats if switched to bpf module.
-        if (mUseBpfStats)
-            return readBpfNetworkStatsDev();
-
-        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
-
-        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
-        final NetworkStats.Entry entry = new NetworkStats.Entry();
-
-        ProcFileReader reader = null;
-        try {
-            reader = new ProcFileReader(new FileInputStream(mStatsXtIfaceAll));
-
-            while (reader.hasMoreData()) {
-                entry.iface = reader.nextString();
-                entry.uid = UID_ALL;
-                entry.set = SET_ALL;
-                entry.tag = TAG_NONE;
-
-                final boolean active = reader.nextInt() != 0;
-
-                // always include snapshot values
-                entry.rxBytes = reader.nextLong();
-                entry.rxPackets = reader.nextLong();
-                entry.txBytes = reader.nextLong();
-                entry.txPackets = reader.nextLong();
-
-                // fold in active numbers, but only when active
-                if (active) {
-                    entry.rxBytes += reader.nextLong();
-                    entry.rxPackets += reader.nextLong();
-                    entry.txBytes += reader.nextLong();
-                    entry.txPackets += reader.nextLong();
-                }
-
-                stats.insertEntry(entry);
-                reader.finishLine();
-            }
-        } catch (NullPointerException|NumberFormatException e) {
-            throw protocolExceptionWithCause("problem parsing stats", e);
-        } finally {
-            IoUtils.closeQuietly(reader);
-            StrictMode.setThreadPolicy(savedPolicy);
-        }
-        return stats;
+        return mDeps.getNetworkStatsDev();
     }
 
     /**
      * Parse and return interface-level summary {@link NetworkStats}. Designed
      * to return only IP layer traffic. Values monotonically increase since
      * device boot, and may include details about inactive interfaces.
-     *
-     * @throws IllegalStateException when problem parsing stats.
      */
     public NetworkStats readNetworkStatsSummaryXt() throws IOException {
-
-        // Return xt_bpf stats if qtaguid  module is replaced.
-        if (mUseBpfStats)
-            return readBpfNetworkStatsDev();
-
-        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
-
-        // return null when kernel doesn't support
-        if (!mStatsXtIfaceFmt.exists()) return null;
-
-        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
-        final NetworkStats.Entry entry = new NetworkStats.Entry();
-
-        ProcFileReader reader = null;
-        try {
-            // open and consume header line
-            reader = new ProcFileReader(new FileInputStream(mStatsXtIfaceFmt));
-            reader.finishLine();
-
-            while (reader.hasMoreData()) {
-                entry.iface = reader.nextString();
-                entry.uid = UID_ALL;
-                entry.set = SET_ALL;
-                entry.tag = TAG_NONE;
-
-                entry.rxBytes = reader.nextLong();
-                entry.rxPackets = reader.nextLong();
-                entry.txBytes = reader.nextLong();
-                entry.txPackets = reader.nextLong();
-
-                stats.insertEntry(entry);
-                reader.finishLine();
-            }
-        } catch (NullPointerException|NumberFormatException e) {
-            throw protocolExceptionWithCause("problem parsing stats", e);
-        } finally {
-            IoUtils.closeQuietly(reader);
-            StrictMode.setThreadPolicy(savedPolicy);
-        }
-        return stats;
+        return mDeps.getNetworkStatsDev();
     }
 
     public NetworkStats readNetworkStatsDetail() throws IOException {
@@ -331,38 +253,14 @@
             // Take a defensive copy. mPersistSnapshot is mutated in some cases below
             final NetworkStats prev = mPersistSnapshot.clone();
 
-            if (USE_NATIVE_PARSING) {
-                final NetworkStats stats =
-                        new NetworkStats(SystemClock.elapsedRealtime(), 0 /* initialSize */);
-                if (mUseBpfStats) {
-                    requestSwapActiveStatsMapLocked();
-                    // Stats are always read from the inactive map, so they must be read after the
-                    // swap
-                    if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL,
-                            INTERFACES_ALL, TAG_ALL, mUseBpfStats) != 0) {
-                        throw new IOException("Failed to parse network stats");
-                    }
-
-                    // BPF stats are incremental; fold into mPersistSnapshot.
-                    mPersistSnapshot.setElapsedRealtime(stats.getElapsedRealtime());
-                    mPersistSnapshot.combineAllValues(stats);
-                } else {
-                    if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL,
-                            INTERFACES_ALL, TAG_ALL, mUseBpfStats) != 0) {
-                        throw new IOException("Failed to parse network stats");
-                    }
-                    if (VALIDATE_NATIVE_STATS) {
-                        final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid,
-                                UID_ALL, INTERFACES_ALL, TAG_ALL);
-                        assertEquals(javaStats, stats);
-                    }
-
-                    mPersistSnapshot = stats;
-                }
-            } else {
-                mPersistSnapshot = javaReadNetworkStatsDetail(mStatsXtUid, UID_ALL, INTERFACES_ALL,
-                        TAG_ALL);
-            }
+            requestSwapActiveStatsMapLocked();
+            // Stats are always read from the inactive map, so they must be read after the
+            // swap
+            final NetworkStats stats = mDeps.getNetworkStatsDetail(
+                    UID_ALL, INTERFACES_ALL, TAG_ALL);
+            // BPF stats are incremental; fold into mPersistSnapshot.
+            mPersistSnapshot.setElapsedRealtime(stats.getElapsedRealtime());
+            mPersistSnapshot.combineAllValues(stats);
 
             NetworkStats adjustedStats = adjustForTunAnd464Xlat(mPersistSnapshot, prev, vpnArray);
 
@@ -399,62 +297,6 @@
         return mTunAnd464xlatAdjustedStats.clone();
     }
 
-    /**
-     * Parse and return {@link NetworkStats} with UID-level details. Values are
-     * expected to monotonically increase since device boot.
-     */
-    @VisibleForTesting
-    public static NetworkStats javaReadNetworkStatsDetail(File detailPath, int limitUid,
-            String[] limitIfaces, int limitTag)
-            throws IOException {
-        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
-
-        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
-        final NetworkStats.Entry entry = new NetworkStats.Entry();
-
-        int idx = 1;
-        int lastIdx = 1;
-
-        ProcFileReader reader = null;
-        try {
-            // open and consume header line
-            reader = new ProcFileReader(new FileInputStream(detailPath));
-            reader.finishLine();
-
-            while (reader.hasMoreData()) {
-                idx = reader.nextInt();
-                if (idx != lastIdx + 1) {
-                    throw new ProtocolException(
-                            "inconsistent idx=" + idx + " after lastIdx=" + lastIdx);
-                }
-                lastIdx = idx;
-
-                entry.iface = reader.nextString();
-                entry.tag = kernelToTag(reader.nextString());
-                entry.uid = reader.nextInt();
-                entry.set = reader.nextInt();
-                entry.rxBytes = reader.nextLong();
-                entry.rxPackets = reader.nextLong();
-                entry.txBytes = reader.nextLong();
-                entry.txPackets = reader.nextLong();
-
-                if ((limitIfaces == null || CollectionUtils.contains(limitIfaces, entry.iface))
-                        && (limitUid == UID_ALL || limitUid == entry.uid)
-                        && (limitTag == TAG_ALL || limitTag == entry.tag)) {
-                    stats.insertEntry(entry);
-                }
-
-                reader.finishLine();
-            }
-        } catch (NullPointerException|NumberFormatException e) {
-            throw protocolExceptionWithCause("problem parsing idx " + idx, e);
-        } finally {
-            IoUtils.closeQuietly(reader);
-            StrictMode.setThreadPolicy(savedPolicy);
-        }
-
-        return stats;
-    }
 
     public void assertEquals(NetworkStats expected, NetworkStats actual) {
         if (expected.size() != actual.size()) {
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 49392e0..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) {
@@ -82,6 +90,13 @@
   return (jint)status.code();
 }
 
+static jint native_setChildChain(JNIEnv* env, jobject self, jint childChain, jboolean enable) {
+  auto chain = static_cast<ChildChain>(childChain);
+  int res = mTc.toggleUidOwnerMap(chain, enable);
+  if (res) ALOGE("%s failed, error code = %d", __func__, res);
+  return (jint)res;
+}
+
 static jint native_replaceUidChain(JNIEnv* env, jobject self, jstring name, jboolean isAllowlist,
                                    jintArray jUids) {
     const ScopedUtfChars chainNameUtf8(env, name);
@@ -192,6 +207,8 @@
     (void*)native_addNiceApp},
     {"native_removeNiceApp", "(I)I",
     (void*)native_removeNiceApp},
+    {"native_setChildChain", "(IZ)I",
+    (void*)native_setChildChain},
     {"native_replaceUidChain", "(Ljava/lang/String;Z[I)I",
     (void*)native_replaceUidChain},
     {"native_setUidRule", "(III)I",
diff --git a/service/native/TrafficController.cpp b/service/native/TrafficController.cpp
index 9331548..303112f 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__);
 
@@ -451,6 +446,53 @@
     return 0;
 }
 
+int TrafficController::toggleUidOwnerMap(ChildChain chain, bool enable) {
+    std::lock_guard guard(mMutex);
+    uint32_t key = UID_RULES_CONFIGURATION_KEY;
+    auto oldConfigure = mConfigurationMap.readValue(key);
+    if (!oldConfigure.ok()) {
+        ALOGE("Cannot read the old configuration from map: %s",
+              oldConfigure.error().message().c_str());
+        return -oldConfigure.error().code();
+    }
+    uint32_t match;
+    switch (chain) {
+        case DOZABLE:
+            match = DOZABLE_MATCH;
+            break;
+        case STANDBY:
+            match = STANDBY_MATCH;
+            break;
+        case POWERSAVE:
+            match = POWERSAVE_MATCH;
+            break;
+        case RESTRICTED:
+            match = RESTRICTED_MATCH;
+            break;
+        case LOW_POWER_STANDBY:
+            match = LOW_POWER_STANDBY_MATCH;
+            break;
+        case OEM_DENY_1:
+            match = OEM_DENY_1_MATCH;
+            break;
+        case OEM_DENY_2:
+            match = OEM_DENY_2_MATCH;
+            break;
+        case OEM_DENY_3:
+            match = OEM_DENY_3_MATCH;
+            break;
+        default:
+            return -EINVAL;
+    }
+    BpfConfig newConfiguration =
+            enable ? (oldConfigure.value() | match) : (oldConfigure.value() & ~match);
+    Status res = mConfigurationMap.writeValue(key, newConfiguration, BPF_EXIST);
+    if (!isOk(res)) {
+        ALOGE("Failed to toggleUidOwnerMap(%d): %s", chain, res.msg().c_str());
+    }
+    return -res.code();
+}
+
 Status TrafficController::swapActiveStatsMap() {
     std::lock_guard guard(mMutex);
 
@@ -572,10 +614,6 @@
     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",
@@ -612,6 +650,8 @@
     ScopedIndent indentForMapContent(dw);
 
     // Print CookieTagMap content.
+    // TagSocketTest in CTS was using the output of mCookieTagMap dump.
+    // So, mCookieTagMap dump can not be removed until the previous CTS support period is over.
     dumpBpfMap("mCookieTagMap", dw, "");
     const auto printCookieTagInfo = [&dw](const uint64_t& key, const UidTagValue& value,
                                           const BpfMap<uint64_t, UidTagValue>&) {
@@ -623,31 +663,6 @@
         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"
diff --git a/service/native/TrafficControllerTest.cpp b/service/native/TrafficControllerTest.cpp
index 7730c13..d08ffee 100644
--- a/service/native/TrafficControllerTest.cpp
+++ b/service/native/TrafficControllerTest.cpp
@@ -793,11 +793,6 @@
     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} {} {} {} {} {} {}",
@@ -834,8 +829,6 @@
 
     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/native/include/TrafficController.h b/service/native/include/TrafficController.h
index 14c5eaf..8512929 100644
--- a/service/native/include/TrafficController.h
+++ b/service/native/include/TrafficController.h
@@ -71,6 +71,8 @@
     netdutils::Status updateUidOwnerMap(const uint32_t uid,
                                         UidOwnerMatchType matchType, IptOp op) EXCLUDES(mMutex);
 
+    int toggleUidOwnerMap(ChildChain chain, bool enable) EXCLUDES(mMutex);
+
     static netdutils::StatusOr<std::unique_ptr<netdutils::NetlinkListenerInterface>>
     makeSkDestroyListener();
 
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index 0ff8810..594223c 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -31,17 +31,19 @@
 import static android.system.OsConstants.ENOENT;
 import static android.system.OsConstants.EOPNOTSUPP;
 
+import android.content.Context;
 import android.net.INetd;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
+import android.provider.DeviceConfig;
 import android.system.ErrnoException;
 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;
+import com.android.net.module.util.DeviceConfigUtils;
 import com.android.net.module.util.Struct.U32;
 
 import java.io.FileDescriptor;
@@ -70,6 +72,10 @@
     // Use legacy netd for releases before T.
     private static boolean sInitialized = false;
 
+    private static Boolean sEnableJavaBpfMap = null;
+    private static final String BPF_NET_MAPS_ENABLE_JAVA_BPF_MAP =
+            "bpf_net_maps_enable_java_bpf_map";
+
     // Lock for sConfigurationMap entry for UID_RULES_CONFIGURATION_KEY.
     // This entry is not accessed by others.
     // BpfNetMaps acquires this lock while sequence of read, modify, and write.
@@ -80,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;
@@ -101,6 +112,14 @@
     // LINT.ThenChange(packages/modules/Connectivity/bpf_progs/bpf_shared.h)
 
     /**
+     * Set sEnableJavaBpfMap for test.
+     */
+    @VisibleForTesting
+    public static void setEnableJavaBpfMapForTest(boolean enable) {
+        sEnableJavaBpfMap = enable;
+    }
+
+    /**
      * Set configurationMap for test.
      */
     @VisibleForTesting
@@ -134,22 +153,47 @@
         }
     }
 
-    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);
+        }
     }
 
     /**
      * Initializes the class if it is not already initialized. This method will open maps but not
      * cause any other effects. This method may be called multiple times on any thread.
      */
-    private static synchronized void ensureInitialized() {
+    private static synchronized void ensureInitialized(final Context context) {
         if (sInitialized) return;
-        setBpfMaps();
+        if (sEnableJavaBpfMap == null) {
+            sEnableJavaBpfMap = DeviceConfigUtils.isFeatureEnabled(context,
+                    DeviceConfig.NAMESPACE_TETHERING, BPF_NET_MAPS_ENABLE_JAVA_BPF_MAP,
+                    SdkLevel.isAtLeastU() /* defaultValue */);
+        }
+        Log.d(TAG, "BpfNetMaps is initialized with sEnableJavaBpfMap=" + sEnableJavaBpfMap);
+
+        initBpfMaps();
         native_init();
         sInitialized = true;
     }
@@ -168,20 +212,20 @@
     }
 
     /** Constructor used after T that doesn't need to use netd anymore. */
-    public BpfNetMaps() {
-        this(null);
+    public BpfNetMaps(final Context context) {
+        this(context, null);
 
         if (PRE_T) throw new IllegalArgumentException("BpfNetMaps need to use netd before T");
     }
 
-    public BpfNetMaps(final INetd netd) {
-        this(netd, new Dependencies());
+    public BpfNetMaps(final Context context, final INetd netd) {
+        this(context, netd, new Dependencies());
     }
 
     @VisibleForTesting
-    public BpfNetMaps(final INetd netd, final Dependencies deps) {
+    public BpfNetMaps(final Context context, final INetd netd, final Dependencies deps) {
         if (!PRE_T) {
-            ensureInitialized();
+            ensureInitialized(context);
         }
         mNetd = netd;
         mDeps = deps;
@@ -320,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");
+        }
     }
 
     /**
@@ -332,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");
+        }
     }
 
     /**
@@ -344,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");
+        }
     }
 
     /**
@@ -356,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");
+        }
     }
 
     /**
@@ -371,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");
         }
     }
 
@@ -418,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));
+            }
         }
     }
 
@@ -463,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");
         }
     }
 
@@ -497,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");
         }
     }
 
@@ -534,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");
         }
     }
 
@@ -553,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");
         }
     }
 
@@ -607,23 +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);
-    @GuardedBy("sUidOwnerMap")
+    private native int native_setChildChain(int childChain, boolean enable);
     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/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index ae1f808..0ca0d83 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -380,7 +380,7 @@
     // See ConnectivitySettingsManager.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
     private final int mReleasePendingIntentDelayMs;
 
-    private MockableSystemProperties mSystemProperties;
+    private final MockableSystemProperties mSystemProperties;
 
     @VisibleForTesting
     protected final PermissionMonitor mPermissionMonitor;
@@ -396,7 +396,7 @@
      * Stale copy of uid blocked reasons provided by NPMS. As long as they are accessed only in
      * internal handler thread, they don't need a lock.
      */
-    private SparseIntArray mUidBlockedReasons = new SparseIntArray();
+    private final SparseIntArray mUidBlockedReasons = new SparseIntArray();
 
     private final Context mContext;
     private final ConnectivityResources mResources;
@@ -412,9 +412,8 @@
     @VisibleForTesting
     protected INetd mNetd;
     private DscpPolicyTracker mDscpPolicyTracker = null;
-    private NetworkStatsManager mStatsManager;
-    private NetworkPolicyManager mPolicyManager;
-    private final NetdCallback mNetdCallback;
+    private final NetworkStatsManager mStatsManager;
+    private final NetworkPolicyManager mPolicyManager;
     private final BpfNetMaps mBpfNetMaps;
 
     /**
@@ -780,7 +779,7 @@
     private boolean mSystemReady;
     private Intent mInitialBroadcast;
 
-    private PowerManager.WakeLock mNetTransitionWakeLock;
+    private final PowerManager.WakeLock mNetTransitionWakeLock;
     private final PowerManager.WakeLock mPendingIntentWakeLock;
 
     // A helper object to track the current default HTTP proxy. ConnectivityService needs to tell
@@ -790,10 +789,10 @@
 
     final private SettingsObserver mSettingsObserver;
 
-    private UserManager mUserManager;
+    private final UserManager mUserManager;
 
     // the set of network types that can only be enabled by system/sig apps
-    private List<Integer> mProtectedNetworks;
+    private final List<Integer> mProtectedNetworks;
 
     private Set<String> mWolSupportedInterfaces;
 
@@ -803,10 +802,10 @@
 
     private final LocationPermissionChecker mLocationPermissionChecker;
 
-    private KeepaliveTracker mKeepaliveTracker;
-    private QosCallbackTracker mQosCallbackTracker;
-    private NetworkNotificationManager mNotifier;
-    private LingerMonitor mLingerMonitor;
+    private final KeepaliveTracker mKeepaliveTracker;
+    private final QosCallbackTracker mQosCallbackTracker;
+    private final NetworkNotificationManager mNotifier;
+    private final LingerMonitor mLingerMonitor;
 
     // sequence number of NetworkRequests
     private int mNextNetworkRequestId = NetworkRequest.FIRST_REQUEST_ID;
@@ -834,7 +833,7 @@
     private final IpConnectivityLog mMetricsLog;
 
     @GuardedBy("mBandwidthRequests")
-    private final SparseArray<Integer> mBandwidthRequests = new SparseArray(10);
+    private final SparseArray<Integer> mBandwidthRequests = new SparseArray<>(10);
 
     @VisibleForTesting
     final MultinetworkPolicyTracker mMultinetworkPolicyTracker;
@@ -893,7 +892,7 @@
          *  - getRestoreTimerForType(type) is also synchronized on mTypeLists.
          *  - dump is thread-safe with respect to concurrent add and remove calls.
          */
-        private final ArrayList<NetworkAgentInfo> mTypeLists[];
+        private final ArrayList<NetworkAgentInfo>[] mTypeLists;
         @NonNull
         private final ConnectivityService mService;
 
@@ -1096,8 +1095,7 @@
             }
         }
 
-        // send out another legacy broadcast - currently only used for suspend/unsuspend
-        // toggle
+        // send out another legacy broadcast - currently only used for suspend/unsuspend toggle
         public void update(NetworkAgentInfo nai) {
             final boolean isDefault = mService.isDefaultNetwork(nai);
             final DetailedState state = nai.networkInfo.getDetailedState();
@@ -1221,31 +1219,25 @@
          */
         public void incrementCountOrThrow(final int uid) {
             synchronized (mUidToNetworkRequestCount) {
-                incrementCountOrThrow(uid, 1 /* numToIncrement */);
+                final int newRequestCount = mUidToNetworkRequestCount.get(uid, 0) + 1;
+                if (newRequestCount >= mMaxCountPerUid
+                        // HACK : the system server is allowed to go over the request count limit
+                        // when it is creating requests on behalf of another app (but not itself,
+                        // so it can still detect its own request leaks). This only happens in the
+                        // per-app API flows in which case the old requests for that particular
+                        // UID will be removed soon.
+                        // TODO : with the removal of the legacy transact() method, exempting the
+                        // system server UID should no longer be necessary. Make sure this is the
+                        // case and remove this test.
+                        && (Process.myUid() == uid || Process.myUid() != Binder.getCallingUid())) {
+                    throw new ServiceSpecificException(
+                            ConnectivityManager.Errors.TOO_MANY_REQUESTS,
+                            "Uid " + uid + " exceeded its allotted requests limit");
+                }
+                mUidToNetworkRequestCount.put(uid, newRequestCount);
             }
         }
 
-        private void incrementCountOrThrow(final int uid, final int numToIncrement) {
-            final int newRequestCount =
-                    mUidToNetworkRequestCount.get(uid, 0) + numToIncrement;
-            if (newRequestCount >= mMaxCountPerUid
-                    // HACK : the system server is allowed to go over the request count limit
-                    // when it is creating requests on behalf of another app (but not itself,
-                    // so it can still detect its own request leaks). This only happens in the
-                    // per-app API flows in which case the old requests for that particular
-                    // UID will be removed soon.
-                    // TODO : instead of this hack, addPerAppDefaultNetworkRequests and other
-                    // users of transact() should unregister the requests to decrease the count
-                    // before they increase it again by creating a new NRI. Then remove the
-                    // transact() method.
-                    && (Process.myUid() == uid || Process.myUid() != Binder.getCallingUid())) {
-                throw new ServiceSpecificException(
-                        ConnectivityManager.Errors.TOO_MANY_REQUESTS,
-                        "Uid " + uid + " exceeded its allotted requests limit");
-            }
-            mUidToNetworkRequestCount.put(uid, newRequestCount);
-        }
-
         /**
          * Decrements the request count of the given uid.
          *
@@ -1253,21 +1245,18 @@
          */
         public void decrementCount(final int uid) {
             synchronized (mUidToNetworkRequestCount) {
-                decrementCount(uid, 1 /* numToDecrement */);
+                /* numToDecrement */
+                final int newRequestCount = mUidToNetworkRequestCount.get(uid, 0) - 1;
+                if (newRequestCount < 0) {
+                    logwtf("BUG: too small request count " + newRequestCount + " for UID " + uid);
+                } else if (newRequestCount == 0) {
+                    mUidToNetworkRequestCount.delete(uid);
+                } else {
+                    mUidToNetworkRequestCount.put(uid, newRequestCount);
+                }
             }
         }
 
-        private void decrementCount(final int uid, final int numToDecrement) {
-            final int newRequestCount =
-                    mUidToNetworkRequestCount.get(uid, 0) - numToDecrement;
-            if (newRequestCount < 0) {
-                logwtf("BUG: too small request count " + newRequestCount + " for UID " + uid);
-            } else if (newRequestCount == 0) {
-                mUidToNetworkRequestCount.delete(uid);
-            } else {
-                mUidToNetworkRequestCount.put(uid, newRequestCount);
-            }
-        }
     }
 
     /**
@@ -1376,7 +1365,11 @@
 
         /**
          * @see CarrierPrivilegeAuthenticator
+         *
+         * This method returns null in versions before T, where carrier privilege
+         * authentication is not supported.
          */
+        @Nullable
         public CarrierPrivilegeAuthenticator makeCarrierPrivilegeAuthenticator(
                 @NonNull final Context context, @NonNull final TelephonyManager tm) {
             if (SdkLevel.isAtLeastT()) {
@@ -1396,11 +1389,11 @@
 
         /**
          * Get the BpfNetMaps implementation to use in ConnectivityService.
-         * @param netd
+         * @param netd a netd binder
          * @return BpfNetMaps implementation.
          */
-        public BpfNetMaps getBpfNetMaps(INetd netd) {
-            return new BpfNetMaps(netd);
+        public BpfNetMaps getBpfNetMaps(Context context, INetd netd) {
+            return new BpfNetMaps(context, netd);
         }
 
         /**
@@ -1529,7 +1522,7 @@
         mProxyTracker = mDeps.makeProxyTracker(mContext, mHandler);
 
         mNetd = netd;
-        mBpfNetMaps = mDeps.getBpfNetMaps(netd);
+        mBpfNetMaps = mDeps.getBpfNetMaps(mContext, netd);
         mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
         mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
         mLocationPermissionChecker = mDeps.makeLocationPermissionChecker(mContext);
@@ -1581,9 +1574,9 @@
 
         mNetworkActivityTracker = new LegacyNetworkActivityTracker(mContext, mHandler, mNetd);
 
-        mNetdCallback = new NetdCallback();
+        final NetdCallback netdCallback = new NetdCallback();
         try {
-            mNetd.registerUnsolicitedEventListener(mNetdCallback);
+            mNetd.registerUnsolicitedEventListener(netdCallback);
         } catch (RemoteException | ServiceSpecificException e) {
             loge("Error registering event listener :" + e);
         }
@@ -1722,11 +1715,6 @@
         mHandler.sendEmptyMessage(EVENT_INGRESS_RATE_LIMIT_CHANGED);
     }
 
-    private void handleAlwaysOnNetworkRequest(NetworkRequest networkRequest, int id) {
-        final boolean enable = mContext.getResources().getBoolean(id);
-        handleAlwaysOnNetworkRequest(networkRequest, enable);
-    }
-
     private void handleAlwaysOnNetworkRequest(
             NetworkRequest networkRequest, String settingName, boolean defaultValue) {
         final boolean enable = toBool(Settings.Global.getInt(
@@ -1769,12 +1757,12 @@
                 Settings.Global.getUriFor(Settings.Global.HTTP_PROXY),
                 EVENT_APPLY_GLOBAL_HTTP_PROXY);
 
-        // Watch for whether or not to keep mobile data always on.
+        // Watch for whether to keep mobile data always on.
         mSettingsObserver.observe(
                 Settings.Global.getUriFor(ConnectivitySettingsManager.MOBILE_DATA_ALWAYS_ON),
                 EVENT_CONFIGURE_ALWAYS_ON_NETWORKS);
 
-        // Watch for whether or not to keep wifi always on.
+        // Watch for whether to keep wifi always on.
         mSettingsObserver.observe(
                 Settings.Global.getUriFor(ConnectivitySettingsManager.WIFI_ALWAYS_REQUESTED),
                 EVENT_CONFIGURE_ALWAYS_ON_NETWORKS);
@@ -1804,6 +1792,7 @@
     }
 
     @VisibleForTesting
+    @Nullable
     protected NetworkAgentInfo getNetworkAgentInfoForNetwork(Network network) {
         if (network == null) {
             return null;
@@ -1818,6 +1807,7 @@
     }
 
     // TODO: determine what to do when more than one VPN applies to |uid|.
+    @Nullable
     private NetworkAgentInfo getVpnForUid(int uid) {
         synchronized (mNetworkForNetId) {
             for (int i = 0; i < mNetworkForNetId.size(); i++) {
@@ -1830,6 +1820,7 @@
         return null;
     }
 
+    @Nullable
     private Network[] getVpnUnderlyingNetworks(int uid) {
         if (mLockdownEnabled) return null;
         final NetworkAgentInfo nai = getVpnForUid(uid);
@@ -1941,6 +1932,7 @@
      * active
      */
     @Override
+    @Nullable
     public NetworkInfo getActiveNetworkInfo() {
         enforceAccessPermission();
         final int uid = mDeps.getCallingUid();
@@ -1952,17 +1944,20 @@
     }
 
     @Override
+    @Nullable
     public Network getActiveNetwork() {
         enforceAccessPermission();
         return getActiveNetworkForUidInternal(mDeps.getCallingUid(), false);
     }
 
     @Override
+    @Nullable
     public Network getActiveNetworkForUid(int uid, boolean ignoreBlocked) {
         enforceNetworkStackPermission(mContext);
         return getActiveNetworkForUidInternal(uid, ignoreBlocked);
     }
 
+    @Nullable
     private Network getActiveNetworkForUidInternal(final int uid, boolean ignoreBlocked) {
         final NetworkAgentInfo vpnNai = getVpnForUid(uid);
         if (vpnNai != null) {
@@ -1981,6 +1976,7 @@
     }
 
     @Override
+    @Nullable
     public NetworkInfo getActiveNetworkInfoForUid(int uid, boolean ignoreBlocked) {
         enforceNetworkStackPermission(mContext);
         final NetworkAgentInfo nai = getNetworkAgentInfoForUid(uid);
@@ -2017,6 +2013,7 @@
     }
 
     @Override
+    @Nullable
     public NetworkInfo getNetworkInfo(int networkType) {
         enforceAccessPermission();
         final int uid = mDeps.getCallingUid();
@@ -2035,6 +2032,7 @@
     }
 
     @Override
+    @Nullable
     public NetworkInfo getNetworkInfoForUid(Network network, int uid, boolean ignoreBlocked) {
         enforceAccessPermission();
         final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
@@ -2057,6 +2055,7 @@
     }
 
     @Override
+    @Nullable
     public Network getNetworkForType(int networkType) {
         enforceAccessPermission();
         if (!mLegacyTypeTracker.isTypeSupported(networkType)) {
@@ -2074,6 +2073,7 @@
     }
 
     @Override
+    @NonNull
     public Network[] getAllNetworks() {
         enforceAccessPermission();
         synchronized (mNetworkForNetId) {
@@ -2534,7 +2534,7 @@
                         snapshot.getNetwork(), snapshot.getSubscriberId()));
             }
         }
-        return result.toArray(new NetworkState[result.size()]);
+        return result.toArray(new NetworkState[0]);
     }
 
     @Override
@@ -2632,7 +2632,7 @@
         try {
             addr = InetAddress.getByAddress(hostAddress);
         } catch (UnknownHostException e) {
-            if (DBG) log("requestRouteToHostAddress got " + e.toString());
+            if (DBG) log("requestRouteToHostAddress got " + e);
             return false;
         }
 
@@ -2643,7 +2643,7 @@
 
         NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
         if (nai == null) {
-            if (mLegacyTypeTracker.isTypeSupported(networkType) == false) {
+            if (!mLegacyTypeTracker.isTypeSupported(networkType)) {
                 if (DBG) log("requestRouteToHostAddress on unsupported network: " + networkType);
             } else {
                 if (DBG) log("requestRouteToHostAddress on down network: " + networkType);
@@ -2736,7 +2736,7 @@
             // the caller thread of registerNetworkAgent. Thus, it's not allowed to register netd
             // event callback for certain nai. e.g. cellular. Register here to pass to
             // NetworkMonitor instead.
-            // TODO: Move the Dns Event to NetworkMonitor. NetdEventListenerService only allow one
+            // TODO: Move the Dns Event to NetworkMonitor. NetdEventListenerService only allows one
             // callback from each caller type. Need to re-factor NetdEventListenerService to allow
             // multiple NetworkMonitor registrants.
             if (nai != null && nai.satisfies(mDefaultRequest.mRequests.get(0))) {
@@ -3101,8 +3101,9 @@
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_ALWAYS_ON_NETWORKS));
 
         // Update mobile data preference if necessary.
-        // Note that empty uid list can be skip here only because no uid rules applied before system
-        // ready. Normally, the empty uid list means to clear the uids rules on netd.
+        // Note that updating can be skipped here if the list is empty only because no uid
+        // rules are applied before system ready. Normally, the empty uid list means to clear
+        // the uids rules on netd.
         if (!ConnectivitySettingsManager.getMobileDataPreferredUids(mContext).isEmpty()) {
             updateMobileDataPreferredUids();
         }
@@ -3216,7 +3217,7 @@
     }
 
     private void dumpNetworkDiagnostics(IndentingPrintWriter pw) {
-        final List<NetworkDiagnostics> netDiags = new ArrayList<NetworkDiagnostics>();
+        final List<NetworkDiagnostics> netDiags = new ArrayList<>();
         final long DIAG_TIME_MS = 5000;
         for (NetworkAgentInfo nai : networksSortedById()) {
             PrivateDnsConfig privateDnsCfg = mDnsManager.getPrivateDnsConfig(nai.network);
@@ -3604,11 +3605,11 @@
 
             switch (msg.what) {
                 case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
-                    final NetworkCapabilities networkCapabilities = new NetworkCapabilities(
-                            (NetworkCapabilities) arg.second);
-                    maybeUpdateWifiRoamTimestamp(nai, networkCapabilities);
-                    processCapabilitiesFromAgent(nai, networkCapabilities);
-                    updateCapabilities(nai.getCurrentScore(), nai, networkCapabilities);
+                    nai.setDeclaredCapabilities((NetworkCapabilities) arg.second);
+                    final NetworkCapabilities sanitized =
+                            nai.getDeclaredCapabilitiesSanitized(mCarrierPrivilegeAuthenticator);
+                    maybeUpdateWifiRoamTimestamp(nai, sanitized);
+                    updateCapabilities(nai.getScore(), nai, sanitized);
                     break;
                 }
                 case NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED: {
@@ -3876,7 +3877,7 @@
                 log(nai.toShortString() + " validation " + (valid ? "passed" : "failed") + logMsg);
             }
             if (valid != nai.lastValidated) {
-                final int oldScore = nai.getCurrentScore();
+                final FullScore oldScore = nai.getScore();
                 nai.lastValidated = valid;
                 nai.everValidated |= valid;
                 updateCapabilities(oldScore, nai, nai.networkCapabilities);
@@ -3952,7 +3953,7 @@
         }
 
         @Override
-        public void handleMessage(Message msg) {
+        public void handleMessage(@NonNull Message msg) {
             if (!maybeHandleNetworkMonitorMessage(msg)
                     && !maybeHandleNetworkAgentInfoMessage(msg)) {
                 maybeHandleNetworkAgentMessage(msg);
@@ -4421,12 +4422,14 @@
                 }
                 config = new NativeNetworkConfig(nai.network.getNetId(), NativeNetworkType.VIRTUAL,
                         INetd.PERMISSION_NONE,
-                        (nai.networkAgentConfig == null || !nai.networkAgentConfig.allowBypass),
+                        !nai.networkAgentConfig.allowBypass /* secure */,
                         getVpnType(nai), nai.networkAgentConfig.excludeLocalRouteVpn);
             } else {
                 config = new NativeNetworkConfig(nai.network.getNetId(), NativeNetworkType.PHYSICAL,
-                        getNetworkPermission(nai.networkCapabilities), /*secure=*/ false,
-                        VpnManager.TYPE_VPN_NONE, /*excludeLocalRoutes=*/ false);
+                        getNetworkPermission(nai.networkCapabilities),
+                        false /* secure */,
+                        VpnManager.TYPE_VPN_NONE,
+                        false /* excludeLocalRoutes */);
             }
             mNetd.networkCreate(config);
             mDnsResolver.createNetworkCache(nai.network.getNetId());
@@ -7259,8 +7262,7 @@
      *         later : see {@link #updateLinkProperties}.
      * @param networkCapabilities the initial capabilites of this network. They can be updated
      *         later : see {@link #updateCapabilities}.
-     * @param initialScore the initial score of the network. See
-     *         {@link NetworkAgentInfo#getCurrentScore}.
+     * @param initialScore the initial score of the network. See {@link NetworkAgentInfo#getScore}.
      * @param networkAgentConfig metadata about the network. This is never updated.
      * @param providerId the ID of the provider owning this NetworkAgent.
      * @return the network created for this agent.
@@ -7295,18 +7297,23 @@
             NetworkScore currentScore, NetworkAgentConfig networkAgentConfig, int providerId,
             int uid) {
 
+        // Make a copy of the passed NI, LP, NC as the caller may hold a reference to them
+        // and mutate them at any time.
+        final NetworkInfo niCopy = new NetworkInfo(networkInfo);
+        final NetworkCapabilities ncCopy = new NetworkCapabilities(networkCapabilities);
+        final LinkProperties lpCopy = new LinkProperties(linkProperties);
+
         // At this point the capabilities/properties are untrusted and unverified, e.g. checks that
-        // the capabilities' access UID comply with security limitations. They will be sanitized
+        // the capabilities' access UIDs comply with security limitations. They will be sanitized
         // as the NAI registration finishes, in handleRegisterNetworkAgent(). This is
         // because some of the checks must happen on the handler thread.
         final NetworkAgentInfo nai = new NetworkAgentInfo(na,
-                new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo),
-                linkProperties, networkCapabilities,
+                new Network(mNetIdManager.reserveNetId()), niCopy, lpCopy, ncCopy,
                 currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig),
                 this, mNetd, mDnsResolver, providerId, uid, mLingerDelayMs,
                 mQosCallbackTracker, mDeps);
 
-        final String extraInfo = networkInfo.getExtraInfo();
+        final String extraInfo = niCopy.getExtraInfo();
         final String name = TextUtils.isEmpty(extraInfo)
                 ? nai.networkCapabilities.getSsid() : extraInfo;
         if (DBG) log("registerNetworkAgent " + nai);
@@ -7321,16 +7328,12 @@
 
     private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) {
         if (VDBG) log("Network Monitor created for " +  nai);
-        // nai.nc and nai.lp are the same object that was passed by the network agent if the agent
-        // lives in the same process as this code (e.g. wifi), so make sure this code doesn't
-        // mutate their object
-        final NetworkCapabilities nc = new NetworkCapabilities(nai.networkCapabilities);
-        final LinkProperties lp = new LinkProperties(nai.linkProperties);
-        // Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says.
-        processCapabilitiesFromAgent(nai, nc);
-        nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc));
-        processLinkPropertiesFromAgent(nai, lp);
-        nai.linkProperties = lp;
+        // Store a copy of the declared capabilities.
+        nai.setDeclaredCapabilities(nai.networkCapabilities);
+        // Make sure the LinkProperties and NetworkCapabilities reflect what the agent info said.
+        nai.getAndSetNetworkCapabilities(mixInCapabilities(nai,
+                nai.getDeclaredCapabilitiesSanitized(mCarrierPrivilegeAuthenticator)));
+        processLinkPropertiesFromAgent(nai, nai.linkProperties);
 
         nai.onNetworkMonitorCreated(networkMonitor);
 
@@ -7791,31 +7794,6 @@
         }
     }
 
-    /**
-     * Called when receiving NetworkCapabilities directly from a NetworkAgent.
-     * Stores into |nai| any data coming from the agent that might also be written to the network's
-     * NetworkCapabilities by ConnectivityService itself. This ensures that the data provided by the
-     * agent is not lost when updateCapabilities is called.
-     */
-    private void processCapabilitiesFromAgent(NetworkAgentInfo nai, NetworkCapabilities nc) {
-        if (nc.hasConnectivityManagedCapability()) {
-            Log.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
-        }
-        // Note: resetting the owner UID before storing the agent capabilities in NAI means that if
-        // the agent attempts to change the owner UID, then nai.declaredCapabilities will not
-        // actually be the same as the capabilities sent by the agent. Still, it is safer to reset
-        // the owner UID here and behave as if the agent had never tried to change it.
-        if (nai.networkCapabilities.getOwnerUid() != nc.getOwnerUid()) {
-            Log.e(TAG, nai.toShortString() + ": ignoring attempt to change owner from "
-                    + nai.networkCapabilities.getOwnerUid() + " to " + nc.getOwnerUid());
-            nc.setOwnerUid(nai.networkCapabilities.getOwnerUid());
-        }
-        nai.declaredCapabilities = new NetworkCapabilities(nc);
-        NetworkAgentInfo.restrictCapabilitiesFromNetworkAgent(nc, nai.creatorUid,
-                mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE),
-                mCarrierPrivilegeAuthenticator);
-    }
-
     /** Modifies |newNc| based on the capabilities of |underlyingNetworks| and |agentCaps|. */
     @VisibleForTesting
     void applyUnderlyingCapabilities(@Nullable Network[] underlyingNetworks,
@@ -7940,7 +7918,8 @@
         }
 
         if (nai.propagateUnderlyingCapabilities()) {
-            applyUnderlyingCapabilities(nai.declaredUnderlyingNetworks, nai.declaredCapabilities,
+            applyUnderlyingCapabilities(nai.declaredUnderlyingNetworks,
+                    nai.getDeclaredCapabilitiesSanitized(mCarrierPrivilegeAuthenticator),
                     newNc);
         }
 
@@ -7982,7 +7961,7 @@
      * @param nai the network having its capabilities updated.
      * @param nc the new network capabilities.
      */
-    private void updateCapabilities(final int oldScore, @NonNull final NetworkAgentInfo nai,
+    private void updateCapabilities(final FullScore oldScore, @NonNull final NetworkAgentInfo nai,
             @NonNull final NetworkCapabilities nc) {
         NetworkCapabilities newNc = mixInCapabilities(nai, nc);
         if (Objects.equals(nai.networkCapabilities, newNc)) return;
@@ -7993,7 +7972,7 @@
         updateAllowedUids(nai, prevNc, newNc);
         nai.updateScoreForNetworkAgentUpdate();
 
-        if (nai.getCurrentScore() == oldScore && newNc.equalRequestableCapabilities(prevNc)) {
+        if (nai.getScore().equals(oldScore) && newNc.equalRequestableCapabilities(prevNc)) {
             // If the requestable capabilities haven't changed, and the score hasn't changed, then
             // the change we're processing can't affect any requests, it can only affect the listens
             // on this network. We might have been called by rematchNetworkAndRequests when a
@@ -8037,7 +8016,7 @@
 
     /** Convenience method to update the capabilities for a given network. */
     private void updateCapabilitiesForNetwork(NetworkAgentInfo nai) {
-        updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
+        updateCapabilities(nai.getScore(), nai, nai.networkCapabilities);
     }
 
     /**
@@ -8347,8 +8326,11 @@
         releasePendingNetworkRequestWithDelay(pendingIntent);
     }
 
+    // networkAgent is only allowed to be null if notificationType is
+    // CALLBACK_UNAVAIL. This is because UNAVAIL is about no network being
+    // available, while all other cases are about some particular network.
     private void callCallbackForRequest(@NonNull final NetworkRequestInfo nri,
-            @NonNull final NetworkAgentInfo networkAgent, final int notificationType,
+            @Nullable final NetworkAgentInfo networkAgent, final int notificationType,
             final int arg1) {
         if (nri.mMessenger == null) {
             // Default request has no msgr. Also prevents callbacks from being invoked for
@@ -8370,14 +8352,13 @@
         switch (notificationType) {
             case ConnectivityManager.CALLBACK_AVAILABLE: {
                 final NetworkCapabilities nc =
-                        networkCapabilitiesRestrictedForCallerPermissions(
-                                networkAgent.networkCapabilities, nri.mPid, nri.mUid);
-                putParcelable(
-                        bundle,
                         createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                nc, includeLocationSensitiveInfo, nri.mPid, nri.mUid,
+                                networkCapabilitiesRestrictedForCallerPermissions(
+                                        networkAgent.networkCapabilities, nri.mPid, nri.mUid),
+                                includeLocationSensitiveInfo, nri.mPid, nri.mUid,
                                 nrForCallback.getRequestorPackageName(),
-                                nri.mCallingAttributionTag));
+                                nri.mCallingAttributionTag);
+                putParcelable(bundle, nc);
                 putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
                         networkAgent.linkProperties, nri.mPid, nri.mUid));
                 // For this notification, arg1 contains the blocked status.
@@ -8831,15 +8812,22 @@
             @NonNull final Set<NetworkRequestInfo> networkRequests) {
         ensureRunningOnConnectivityServiceThread();
         // TODO: This may be slow, and should be optimized.
-        final long now = SystemClock.elapsedRealtime();
+        final long start = SystemClock.elapsedRealtime();
         final NetworkReassignment changes = computeNetworkReassignment(networkRequests);
+        final long computed = SystemClock.elapsedRealtime();
+        applyNetworkReassignment(changes, start);
+        final long applied = SystemClock.elapsedRealtime();
+        issueNetworkNeeds();
+        final long end = SystemClock.elapsedRealtime();
         if (VDBG || DDBG) {
+            log(String.format("Rematched networks [computed %dms] [applied %dms] [issued %d]",
+                    computed - start, applied - computed, end - applied));
             log(changes.debugString());
         } else if (DBG) {
-            log(changes.toString()); // Shorter form, only one line of log
+            // Shorter form, only one line of log
+            log(String.format("%s [c %d] [a %d] [i %d]", changes.toString(),
+                    computed - start, applied - computed, end - applied));
         }
-        applyNetworkReassignment(changes, now);
-        issueNetworkNeeds();
     }
 
     private void applyNetworkReassignment(@NonNull final NetworkReassignment changes,
@@ -9511,9 +9499,7 @@
         final UnderlyingNetworkInfo[] underlyingNetworkInfos = getAllVpnInfo();
         try {
             final ArrayList<NetworkStateSnapshot> snapshots = new ArrayList<>();
-            for (final NetworkStateSnapshot snapshot : getAllNetworkStateSnapshots()) {
-                snapshots.add(snapshot);
-            }
+            snapshots.addAll(getAllNetworkStateSnapshots());
             mStatsManager.notifyNetworkStatus(getDefaultNetworks(),
                     snapshots, activeIface, Arrays.asList(underlyingNetworkInfos));
         } catch (Exception ignored) {
@@ -9900,14 +9886,12 @@
     private static class NetworkTestedResults {
         private final int mNetId;
         private final int mTestResult;
-        private final long mTimestampMillis;
         @Nullable private final String mRedirectUrl;
 
         private NetworkTestedResults(
                 int netId, int testResult, long timestampMillis, @Nullable String redirectUrl) {
             mNetId = netId;
             mTestResult = testResult;
-            mTimestampMillis = timestampMillis;
             mRedirectUrl = redirectUrl;
         }
     }
@@ -10331,14 +10315,14 @@
         }
 
         @Override
-        public void onInterfaceLinkStateChanged(String iface, boolean up) {
+        public void onInterfaceLinkStateChanged(@NonNull String iface, boolean up) {
             for (NetworkAgentInfo nai : mNetworkAgentInfos) {
                 nai.clatd.interfaceLinkStateChanged(iface, up);
             }
         }
 
         @Override
-        public void onInterfaceRemoved(String iface) {
+        public void onInterfaceRemoved(@NonNull String iface) {
             for (NetworkAgentInfo nai : mNetworkAgentInfos) {
                 nai.clatd.interfaceRemoved(iface);
             }
@@ -10361,10 +10345,10 @@
         @GuardedBy("mActiveIdleTimers")
         private boolean mNetworkActive;
         @GuardedBy("mActiveIdleTimers")
-        private final ArrayMap<String, IdleTimerParams> mActiveIdleTimers = new ArrayMap();
+        private final ArrayMap<String, IdleTimerParams> mActiveIdleTimers = new ArrayMap<>();
         private final Handler mHandler;
 
-        private class IdleTimerParams {
+        private static class IdleTimerParams {
             public final int timeout;
             public final int transportType;
 
@@ -10410,7 +10394,7 @@
                     try {
                         mNetworkActivityListeners.getBroadcastItem(i).onNetworkActive();
                     } catch (RemoteException | RuntimeException e) {
-                        loge("Fail to send network activie to listener " + e);
+                        loge("Fail to send network activity to listener " + e);
                     }
                 }
             } finally {
@@ -10631,8 +10615,8 @@
     @VisibleForTesting
     public void registerQosCallbackInternal(@NonNull final QosFilter filter,
             @NonNull final IQosCallback callback, @NonNull final NetworkAgentInfo nai) {
-        if (filter == null) throw new IllegalArgumentException("filter must be non-null");
-        if (callback == null) throw new IllegalArgumentException("callback must be non-null");
+        Objects.requireNonNull(filter, "filter must be non-null");
+        Objects.requireNonNull(callback, "callback must be non-null");
 
         if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
             // TODO: Check allowed list here and ensure that either a) any QoS callback registered
@@ -10710,8 +10694,7 @@
                     + "or the device owner must be set. ");
         }
 
-        final List<ProfileNetworkPreferenceList.Preference> preferenceList =
-                new ArrayList<ProfileNetworkPreferenceList.Preference>();
+        final List<ProfileNetworkPreferenceList.Preference> preferenceList = new ArrayList<>();
         boolean hasDefaultPreference = false;
         for (final ProfileNetworkPreference preference : preferences) {
             final NetworkCapabilities nc;
@@ -10792,7 +10775,7 @@
                 uidRangeSet = UidRangeUtils.removeRangeSetFromUidRange(profileUids,
                         disallowUidRangeSet);
             } else {
-                uidRangeSet = new ArraySet<UidRange>();
+                uidRangeSet = new ArraySet<>();
                 uidRangeSet.add(profileUids);
             }
         }
@@ -10801,8 +10784,7 @@
 
     private boolean isEnterpriseIdentifierValid(
             @NetworkCapabilities.EnterpriseId int identifier) {
-        if ((identifier >= NET_ENTERPRISE_ID_1)
-                && (identifier <= NET_ENTERPRISE_ID_5)) {
+        if ((identifier >= NET_ENTERPRISE_ID_1) && (identifier <= NET_ENTERPRISE_ID_5)) {
             return true;
         }
         return false;
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/service/src/com/android/server/connectivity/FullScore.java b/service/src/com/android/server/connectivity/FullScore.java
index b156045..c4754eb 100644
--- a/service/src/com/android/server/connectivity/FullScore.java
+++ b/service/src/com/android/server/connectivity/FullScore.java
@@ -49,10 +49,6 @@
 public class FullScore {
     private static final String TAG = FullScore.class.getSimpleName();
 
-    // This will be removed soon. Do *NOT* depend on it for any new code that is not part of
-    // a migration.
-    private final int mLegacyInt;
-
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"POLICY_"}, value = {
@@ -146,9 +142,7 @@
 
     private final int mKeepConnectedReason;
 
-    FullScore(final int legacyInt, final long policies,
-            @KeepConnectedReason final int keepConnectedReason) {
-        mLegacyInt = legacyInt;
+    FullScore(final long policies, @KeepConnectedReason final int keepConnectedReason) {
         mPolicies = policies;
         mKeepConnectedReason = keepConnectedReason;
     }
@@ -170,7 +164,7 @@
     public static FullScore fromNetworkScore(@NonNull final NetworkScore score,
             @NonNull final NetworkCapabilities caps, @NonNull final NetworkAgentConfig config,
             final boolean everValidated, final boolean yieldToBadWiFi, final boolean destroyed) {
-        return withPolicies(score.getLegacyInt(), score.getPolicies(),
+        return withPolicies(score.getPolicies(),
                 score.getKeepConnectedReason(),
                 caps.hasCapability(NET_CAPABILITY_VALIDATED),
                 caps.hasTransport(TRANSPORT_VPN),
@@ -216,7 +210,7 @@
         // A prospective score is invincible if the legacy int in the filter is over the maximum
         // score.
         final boolean invincible = score.getLegacyInt() > NetworkRanker.LEGACY_INT_MAX;
-        return withPolicies(score.getLegacyInt(), score.getPolicies(), KEEP_CONNECTED_NONE,
+        return withPolicies(score.getPolicies(), KEEP_CONNECTED_NONE,
                 mayValidate, vpn, unmetered, everValidated, everUserSelected, acceptUnvalidated,
                 yieldToBadWiFi, destroyed, invincible);
     }
@@ -236,7 +230,7 @@
             final boolean everValidated,
             final boolean yieldToBadWifi,
             final boolean destroyed) {
-        return withPolicies(mLegacyInt, mPolicies, mKeepConnectedReason,
+        return withPolicies(mPolicies, mKeepConnectedReason,
                 caps.hasCapability(NET_CAPABILITY_VALIDATED),
                 caps.hasTransport(TRANSPORT_VPN),
                 caps.hasCapability(NET_CAPABILITY_NOT_METERED),
@@ -251,8 +245,7 @@
     // TODO : this shouldn't manage bad wifi avoidance – instead this should be done by the
     // telephony factory, so that it depends on the carrier. For now this is handled by
     // connectivity for backward compatibility.
-    private static FullScore withPolicies(@NonNull final int legacyInt,
-            final long externalPolicies,
+    private static FullScore withPolicies(final long externalPolicies,
             @KeepConnectedReason final int keepConnectedReason,
             final boolean isValidated,
             final boolean isVpn,
@@ -263,7 +256,7 @@
             final boolean yieldToBadWiFi,
             final boolean destroyed,
             final boolean invincible) {
-        return new FullScore(legacyInt, (externalPolicies & EXTERNAL_POLICIES_MASK)
+        return new FullScore((externalPolicies & EXTERNAL_POLICIES_MASK)
                 | (isValidated       ? 1L << POLICY_IS_VALIDATED : 0)
                 | (isVpn             ? 1L << POLICY_IS_VPN : 0)
                 | (isUnmetered       ? 1L << POLICY_IS_UNMETERED : 0)
@@ -280,8 +273,7 @@
      * Returns this score but with the specified yield to bad wifi policy.
      */
     public FullScore withYieldToBadWiFi(final boolean newYield) {
-        return new FullScore(mLegacyInt,
-                newYield ? mPolicies | (1L << POLICY_YIELD_TO_BAD_WIFI)
+        return new FullScore(newYield ? mPolicies | (1L << POLICY_YIELD_TO_BAD_WIFI)
                         : mPolicies & ~(1L << POLICY_YIELD_TO_BAD_WIFI),
                 mKeepConnectedReason);
     }
@@ -290,49 +282,7 @@
      * Returns this score but validated.
      */
     public FullScore asValidated() {
-        return new FullScore(mLegacyInt, mPolicies | (1L << POLICY_IS_VALIDATED),
-                mKeepConnectedReason);
-    }
-
-    /**
-     * For backward compatibility, get the legacy int.
-     * This will be removed before S is published.
-     */
-    public int getLegacyInt() {
-        return getLegacyInt(false /* pretendValidated */);
-    }
-
-    public int getLegacyIntAsValidated() {
-        return getLegacyInt(true /* pretendValidated */);
-    }
-
-    // TODO : remove these two constants
-    // Penalty applied to scores of Networks that have not been validated.
-    private static final int UNVALIDATED_SCORE_PENALTY = 40;
-
-    // Score for a network that can be used unvalidated
-    private static final int ACCEPT_UNVALIDATED_NETWORK_SCORE = 100;
-
-    private int getLegacyInt(boolean pretendValidated) {
-        // If the user has chosen this network at least once, give it the maximum score when
-        // checking to pretend it's validated, or if it doesn't need to validate because the
-        // user said to use it even if it doesn't validate.
-        // This ensures that networks that have been selected in UI are not torn down before the
-        // user gets a chance to prefer it when a higher-scoring network (e.g., Ethernet) is
-        // available.
-        if (hasPolicy(POLICY_EVER_USER_SELECTED)
-                && (hasPolicy(POLICY_ACCEPT_UNVALIDATED) || pretendValidated)) {
-            return ACCEPT_UNVALIDATED_NETWORK_SCORE;
-        }
-
-        int score = mLegacyInt;
-        // Except for VPNs, networks are subject to a penalty for not being validated.
-        // Apply the penalty unless the network is a VPN, or it's validated or pretending to be.
-        if (!hasPolicy(POLICY_IS_VALIDATED) && !pretendValidated && !hasPolicy(POLICY_IS_VPN)) {
-            score -= UNVALIDATED_SCORE_PENALTY;
-        }
-        if (score < 0) score = 0;
-        return score;
+        return new FullScore(mPolicies | (1L << POLICY_IS_VALIDATED), mKeepConnectedReason);
     }
 
     /**
@@ -350,15 +300,32 @@
         return mKeepConnectedReason;
     }
 
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        final FullScore fullScore = (FullScore) o;
+
+        if (mPolicies != fullScore.mPolicies) return false;
+        return mKeepConnectedReason == fullScore.mKeepConnectedReason;
+    }
+
+    @Override
+    public int hashCode() {
+        return 2 * ((int) mPolicies)
+                + 3 * (int) (mPolicies >>> 32)
+                + 5 * mKeepConnectedReason;
+    }
+
     // Example output :
-    // Score(50 ; Policies : EVER_USER_SELECTED&IS_VALIDATED)
+    // Score(Policies : EVER_USER_SELECTED&IS_VALIDATED ; KeepConnected : )
     @Override
     public String toString() {
         final StringJoiner sj = new StringJoiner(
                 "&", // delimiter
-                "Score(" + mLegacyInt + " ; KeepConnected : " + mKeepConnectedReason
-                        + " ; Policies : ", // prefix
-                ")"); // suffix
+                "Score(Policies : ", // prefix
+                " ; KeepConnected : " + mKeepConnectedReason + ")"); // suffix
         for (int i = NetworkScore.MIN_AGENT_MANAGED_POLICY;
                 i <= NetworkScore.MAX_AGENT_MANAGED_POLICY; ++i) {
             if (hasPolicy(i)) sj.add(policyNameOf(i));
diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
index b40b6e0..04f378f 100644
--- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
@@ -26,6 +26,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.net.CaptivePortalData;
 import android.net.DscpPolicy;
 import android.net.IDnsResolver;
@@ -66,6 +67,7 @@
 import com.android.server.ConnectivityService;
 
 import java.io.PrintWriter;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -104,7 +106,7 @@
 //    for example:
 //    a. a captive portal is present, or
 //    b. a WiFi router whose Internet backhaul is down, or
-//    c. a wireless connection stops transfering packets temporarily (e.g. device is in elevator
+//    c. a wireless connection stops transferring packets temporarily (e.g. device is in elevator
 //       or tunnel) but does not disconnect from the AP/cell tower, or
 //    d. a stand-alone device offering a WiFi AP without an uplink for configuration purposes.
 // 5. registered, created, connected, validated
@@ -157,7 +159,7 @@
 // the network is no longer considered "lingering". After the linger timer expires, if the network
 // is satisfying one or more background NetworkRequests it is kept up in the background. If it is
 // not, ConnectivityService disconnects the NetworkAgent's AsyncChannel.
-public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRanker.Scoreable {
+public class NetworkAgentInfo implements NetworkRanker.Scoreable {
 
     @NonNull public NetworkInfo networkInfo;
     // This Network object should always be used if possible, so as to encourage reuse of the
@@ -181,8 +183,11 @@
 
     // The capabilities originally announced by the NetworkAgent, regardless of any capabilities
     // that were added or removed due to this network's underlying networks.
-    // Only set if #propagateUnderlyingCapabilities is true.
-    public @Nullable NetworkCapabilities declaredCapabilities;
+    //
+    // As the name implies, these capabilities are not sanitized and are not to
+    // be trusted. Most callers should simply use the {@link networkCapabilities}
+    // field instead.
+    private @Nullable NetworkCapabilities mDeclaredCapabilitiesUnsanitized;
 
     // Indicates if netd has been told to create this Network. From this point on the appropriate
     // routing rules are setup and routes are added so packets can begin flowing over the Network.
@@ -236,6 +241,53 @@
     // URL, Terms & Conditions URL, and network friendly name.
     public CaptivePortalData networkAgentPortalData;
 
+    /**
+     * Sets the capabilities sent by the agent for later retrieval.
+     *
+     * This method does not sanitize the capabilities ; instead, use
+     * {@link #getDeclaredCapabilitiesSanitized} to retrieve a sanitized
+     * copy of the capabilities as they were passed here.
+     *
+     * This method makes a defensive copy to avoid issues where the passed object is later mutated.
+     *
+     * @param caps the caps sent by the agent
+     */
+    public void setDeclaredCapabilities(@NonNull final NetworkCapabilities caps) {
+        mDeclaredCapabilitiesUnsanitized = new NetworkCapabilities(caps);
+    }
+
+    /**
+     * Get the latest capabilities sent by the network agent, after sanitizing them.
+     *
+     * These are the capabilities as they were sent by the agent (but sanitized to conform to
+     * their restrictions). They are NOT the capabilities currently applying to this agent ;
+     * for that, use {@link #networkCapabilities}.
+     *
+     * Agents have restrictions on what capabilities they can send to Connectivity. For example,
+     * they can't change the owner UID from what they declared before, and complex restrictions
+     * apply to the allowedUids field.
+     * They also should not mutate immutable capabilities, although for backward-compatibility
+     * this is not enforced and limited to just a log.
+     *
+     * @param carrierPrivilegeAuthenticator the authenticator, to check access UIDs.
+     */
+    public NetworkCapabilities getDeclaredCapabilitiesSanitized(
+            final CarrierPrivilegeAuthenticator carrierPrivilegeAuthenticator) {
+        final NetworkCapabilities nc = new NetworkCapabilities(mDeclaredCapabilitiesUnsanitized);
+        if (nc.hasConnectivityManagedCapability()) {
+            Log.wtf(TAG, "BUG: " + this + " has CS-managed capability.");
+        }
+        if (networkCapabilities.getOwnerUid() != nc.getOwnerUid()) {
+            Log.e(TAG, toShortString() + ": ignoring attempt to change owner from "
+                    + networkCapabilities.getOwnerUid() + " to " + nc.getOwnerUid());
+            nc.setOwnerUid(networkCapabilities.getOwnerUid());
+        }
+        restrictCapabilitiesFromNetworkAgent(nc, creatorUid,
+                mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE),
+                carrierPrivilegeAuthenticator);
+        return nc;
+    }
+
     // Networks are lingered when they become unneeded as a result of their NetworkRequests being
     // satisfied by a higher-scoring network. so as to allow communication to wrap up before the
     // network is taken down.  This usually only happens to the default network. Lingering ends with
@@ -366,6 +418,8 @@
     private final Handler mHandler;
     private final QosCallbackTracker mQosCallbackTracker;
 
+    private final long mCreationTime;
+
     public NetworkAgentInfo(INetworkAgent na, Network net, NetworkInfo info,
             @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc,
             @NonNull NetworkScore score, Context context,
@@ -398,6 +452,7 @@
         declaredUnderlyingNetworks = (nc.getUnderlyingNetworks() != null)
                 ? nc.getUnderlyingNetworks().toArray(new Network[0])
                 : null;
+        mCreationTime = System.currentTimeMillis();
     }
 
     private class AgentDeathMonitor implements IBinder.DeathRecipient {
@@ -963,18 +1018,6 @@
         return mScore;
     }
 
-    // Get the current score for this Network.  This may be modified from what the
-    // NetworkAgent sent, as it has modifiers applied to it.
-    public int getCurrentScore() {
-        return mScore.getLegacyInt();
-    }
-
-    // Get the current score for this Network as if it was validated.  This may be modified from
-    // what the NetworkAgent sent, as it has modifiers applied to it.
-    public int getCurrentScoreAsValidated() {
-        return mScore.getLegacyIntAsValidated();
-    }
-
     /**
      * Mix-in the ConnectivityService-managed bits in the score.
      */
@@ -1279,6 +1322,7 @@
         return "NetworkAgentInfo{"
                 + "network{" + network + "}  handle{" + network.getNetworkHandle() + "}  ni{"
                 + networkInfo.toShortString() + "} "
+                + "created=" + Instant.ofEpochMilli(mCreationTime) + " "
                 + mScore + " "
                 + (created ? " created" : "")
                 + (destroyed ? " destroyed" : "")
@@ -1312,12 +1356,6 @@
                 + transportNamesOf(networkCapabilities.getTransportTypes()) + "]";
     }
 
-    // Enables sorting in descending order of score.
-    @Override
-    public int compareTo(NetworkAgentInfo other) {
-        return other.getCurrentScore() - getCurrentScore();
-    }
-
     /**
      * Null-guarding version of NetworkAgentInfo#toShortString()
      */
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index 62f37bb..a6179fc 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -128,3 +128,18 @@
     ],
 }
 
+android_test {
+    name: "CtsNetTestCasesMaxTargetSdk30",  // Must match CtsNetTestCasesMaxTargetSdk30 annotation.
+    defaults: [
+        "CtsNetTestCasesDefaults",
+        "CtsNetTestCasesApiStableDefaults",
+    ],
+    target_sdk_version: "30",
+    package_name: "android.net.cts.maxtargetsdk30",  // CTS package names must be unique.
+    instrumentation_target_package: "android.net.cts.maxtargetsdk30",
+    test_suites: [
+        "cts",
+        "general-tests",
+        "mts-networking",
+    ],
+}
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 64238b3..6ff2458 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -189,6 +189,7 @@
 import com.android.networkstack.apishim.NetworkInformationShimImpl;
 import com.android.networkstack.apishim.common.ConnectivityManagerShim;
 import com.android.testutils.CompatUtil;
+import com.android.testutils.ConnectivityModuleTest;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 import com.android.testutils.DeviceInfoUtils;
@@ -412,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
@@ -3358,7 +3362,7 @@
         }, NETWORK_SETTINGS);
     }
 
-    @Test @IgnoreUpTo(SC_V2)
+    @Test @IgnoreUpTo(SC_V2) @ConnectivityModuleTest
     @AppModeFull(reason = "Socket cannot bind in instant app mode")
     public void testFirewallBlocking() {
         // ALLOWLIST means the firewall denies all by default, uids must be explicitly allowed
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 b68d3bf..1f76773 100644
--- a/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
+++ b/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
@@ -16,10 +16,8 @@
 
 package android.net.cts
 
-import android.net.cts.util.CtsNetUtils.TestNetworkCallback
-
-import android.app.Instrumentation
 import android.Manifest.permission.MANAGE_TEST_NETWORKS
+import android.app.Instrumentation
 import android.content.Context
 import android.net.ConnectivityManager
 import android.net.DscpPolicy
@@ -27,8 +25,8 @@
 import android.net.IpPrefix
 import android.net.LinkAddress
 import android.net.LinkProperties
-import android.net.Network
 import android.net.MacAddress
+import android.net.Network
 import android.net.NetworkAgent
 import android.net.NetworkAgent.DSCP_POLICY_STATUS_DELETED
 import android.net.NetworkAgent.DSCP_POLICY_STATUS_SUCCESS
@@ -43,9 +41,10 @@
 import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
 import android.net.NetworkCapabilities.TRANSPORT_TEST
 import android.net.NetworkRequest
+import android.net.RouteInfo
 import android.net.TestNetworkInterface
 import android.net.TestNetworkManager
-import android.net.RouteInfo
+import android.net.cts.util.CtsNetUtils.TestNetworkCallback
 import android.os.HandlerThread
 import android.os.SystemClock
 import android.platform.test.annotations.AppModeFull
@@ -61,28 +60,24 @@
 import android.util.Range
 import androidx.test.InstrumentationRegistry
 import androidx.test.runner.AndroidJUnit4
+import com.android.net.module.util.IpUtils
 import com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV4
 import com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6
 import com.android.net.module.util.Struct
 import com.android.net.module.util.structs.EthernetHeader
 import com.android.testutils.ArpResponder
 import com.android.testutils.CompatUtil
+import com.android.testutils.ConnectivityModuleTest
 import com.android.testutils.DevSdkIgnoreRule
-import com.android.testutils.assertParcelingIsLossless
 import com.android.testutils.RouterAdvertisementResponder
-import com.android.testutils.runAsShell
 import com.android.testutils.SC_V2
 import com.android.testutils.TapPacketReader
 import com.android.testutils.TestableNetworkAgent
-import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkCreated
 import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnDscpPolicyStatusUpdated
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkCreated
 import com.android.testutils.TestableNetworkCallback
-import org.junit.After
-import org.junit.Assume.assumeTrue
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
+import com.android.testutils.assertParcelingIsLossless
+import com.android.testutils.runAsShell
 import java.net.Inet4Address
 import java.net.Inet6Address
 import java.net.InetSocketAddress
@@ -93,6 +88,12 @@
 import kotlin.test.assertNotNull
 import kotlin.test.assertTrue
 import kotlin.test.fail
+import org.junit.After
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
 
 private const val MAX_PACKET_LENGTH = 1500
 
@@ -107,6 +108,7 @@
 
 @AppModeFull(reason = "Instant apps cannot create test networks")
 @RunWith(AndroidJUnit4::class)
+@ConnectivityModuleTest
 class DscpPolicyTest {
     @JvmField
     @Rule
@@ -218,33 +220,32 @@
 
     private fun waitForGlobalIpv6Address(network: Network): Inet6Address {
         // Wait for global IPv6 address to be available
-        val sock = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)
-        network.bindSocket(sock)
-
         var inet6Addr: Inet6Address? = null
-        val timeout = SystemClock.elapsedRealtime() + PACKET_TIMEOUT_MS
-        while (timeout > SystemClock.elapsedRealtime()) {
+        val onLinkPrefix = raResponder.prefix
+        val startTime = SystemClock.elapsedRealtime()
+        while (SystemClock.elapsedRealtime() - startTime < PACKET_TIMEOUT_MS) {
+            SystemClock.sleep(1 /* ms */)
+            val sock = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)
             try {
-                // Pick any arbitrary port
-                Os.connect(sock, TEST_TARGET_IPV6_ADDR, 12345)
-                val sockAddr = Os.getsockname(sock) as InetSocketAddress
+                network.bindSocket(sock)
 
-                // 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)) {
+                try {
+                    // Pick any arbitrary port
+                    Os.connect(sock, TEST_TARGET_IPV6_ADDR, 12345)
+                } catch (e: ErrnoException) {
+                    // there may not be an address available yet.
+                    if (e.errno == ENETUNREACH) continue
+                    throw e
+                }
+                val sockAddr = Os.getsockname(sock) as InetSocketAddress
+                if (onLinkPrefix.contains(sockAddr.address)) {
                     inet6Addr = sockAddr.address as Inet6Address
                     break
                 }
-            } catch (e: ErrnoException) {
-                // ignore ENETUNREACH -- there may not be an address available yet.
-                if (e.errno != ENETUNREACH) {
-                    Os.close(sock)
-                    throw e
-                }
+            } finally {
+                Os.close(sock)
             }
-            SystemClock.sleep(10 /* ms */)
         }
-        Os.close(sock)
         assertNotNull(inet6Addr)
         return inet6Addr!!
     }
@@ -315,6 +316,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()
@@ -323,6 +327,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)
     }
 
@@ -397,6 +403,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
@@ -440,6 +447,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 {
@@ -478,6 +488,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..ce8584f 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
@@ -79,6 +80,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import java.net.Inet6Address
+import java.util.Random
 import java.util.concurrent.CompletableFuture
 import java.util.concurrent.ExecutionException
 import java.util.concurrent.TimeUnit
@@ -117,6 +119,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 {
 
@@ -153,8 +156,9 @@
             val mtu = tapInterface.mtu
             packetReader = TapPacketReader(handler, tapInterface.fileDescriptor.fileDescriptor, mtu)
             raResponder = RouterAdvertisementResponder(packetReader)
-            raResponder.addRouterEntry(MacAddress.fromString("01:23:45:67:89:ab"),
-                    InetAddresses.parseNumericAddress("fe80::abcd") as Inet6Address)
+            val iidString = "fe80::${Integer.toHexString(Random().nextInt(65536))}"
+            val linklocal = InetAddresses.parseNumericAddress(iidString) as Inet6Address
+            raResponder.addRouterEntry(MacAddress.fromString("01:23:45:67:89:ab"), linklocal)
 
             packetReader.startAsyncForTest()
             raResponder.start()
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index 64cc97d..a02be85 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -16,6 +16,7 @@
 package android.net.cts
 
 import android.Manifest.permission.MANAGE_TEST_NETWORKS
+import android.app.compat.CompatChanges
 import android.net.ConnectivityManager
 import android.net.ConnectivityManager.NetworkCallback
 import android.net.LinkProperties
@@ -46,6 +47,7 @@
 import android.net.nsd.NsdManager.RegistrationListener
 import android.net.nsd.NsdManager.ResolveListener
 import android.net.nsd.NsdServiceInfo
+import android.os.Build
 import android.os.Handler
 import android.os.HandlerThread
 import android.os.Process.myTid
@@ -56,17 +58,23 @@
 import com.android.net.module.util.ArrayTrackRecord
 import com.android.net.module.util.TrackRecord
 import com.android.networkstack.apishim.NsdShimImpl
+import com.android.testutils.ConnectivityModuleTest
+import com.android.testutils.DevSdkIgnoreRule
 import com.android.testutils.TestableNetworkAgent
 import com.android.testutils.TestableNetworkCallback
+import com.android.testutils.filters.CtsNetTestCasesMaxTargetSdk30
 import com.android.testutils.runAsShell
 import com.android.testutils.tryTest
 import org.junit.After
 import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Assume.assumeTrue
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import java.io.File
 import java.net.ServerSocket
 import java.nio.charset.StandardCharsets
 import java.util.Random
@@ -89,6 +97,10 @@
 @AppModeFull(reason = "Socket cannot bind in instant app mode")
 @RunWith(AndroidJUnit4::class)
 class NsdManagerTest {
+    // Rule used to filter CtsNetTestCasesMaxTargetSdkXX
+    @get:Rule
+    val ignoreRule = DevSdkIgnoreRule()
+
     private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
     private val nsdManager by lazy { context.getSystemService(NsdManager::class.java) }
 
@@ -692,6 +704,30 @@
         }
     }
 
+    @Test @CtsNetTestCasesMaxTargetSdk30("Socket is started with the service up to target SDK 30")
+    fun testManagerCreatesLegacySocket() {
+        nsdManager // Ensure the lazy-init member is initialized, so NsdManager is created
+        val socket = File("/dev/socket/mdnsd")
+        val timeout = System.currentTimeMillis() + TIMEOUT_MS
+        while (!socket.exists() && System.currentTimeMillis() < timeout) {
+            Thread.sleep(10)
+        }
+        assertTrue("$socket was not found after $TIMEOUT_MS ms", socket.exists())
+    }
+
+    // The compat change is part of a connectivity module update that applies to T+
+    @ConnectivityModuleTest @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
+    @Test @CtsNetTestCasesMaxTargetSdk30("Socket is started with the service up to target SDK 30")
+    fun testManagerCreatesLegacySocket_CompatChange() {
+        // The socket may have been already created by some other app, or some other test, in which
+        // case this test cannot verify creation. At least verify that the compat change is
+        // disabled in a process with max SDK 30; unit tests already verify that start is requested
+        // when the compat change is disabled.
+        // Note that before T the compat constant had a different int value.
+        assertFalse(CompatChanges.isChangeEnabled(
+                NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER))
+    }
+
     /**
      * Register a service and return its registration record.
      */
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/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index efc24d3..73e4c0e 100644
--- a/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -209,7 +209,7 @@
         doReturn(mock(ProxyTracker::class.java)).`when`(deps).makeProxyTracker(any(), any())
         doReturn(mock(MockableSystemProperties::class.java)).`when`(deps).systemProperties
         doReturn(TestNetIdManager()).`when`(deps).makeNetIdManager()
-        doReturn(mock(BpfNetMaps::class.java)).`when`(deps).getBpfNetMaps(any())
+        doReturn(mock(BpfNetMaps::class.java)).`when`(deps).getBpfNetMaps(any(), any())
         doAnswer { inv ->
             object : MultinetworkPolicyTracker(inv.getArgument(0), inv.getArgument(1),
                     inv.getArgument(2)) {
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/Android.bp b/tests/native/connectivity_native_test/Android.bp
similarity index 100%
rename from tests/native/Android.bp
rename to tests/native/connectivity_native_test/Android.bp
diff --git a/tests/native/AndroidTestTemplate.xml b/tests/native/connectivity_native_test/AndroidTestTemplate.xml
similarity index 100%
rename from tests/native/AndroidTestTemplate.xml
rename to tests/native/connectivity_native_test/AndroidTestTemplate.xml
diff --git a/tests/native/NetNativeTestConfigTemplate.xml b/tests/native/connectivity_native_test/NetNativeTestConfigTemplate.xml
similarity index 100%
rename from tests/native/NetNativeTestConfigTemplate.xml
rename to tests/native/connectivity_native_test/NetNativeTestConfigTemplate.xml
diff --git a/tests/native/OWNERS b/tests/native/connectivity_native_test/OWNERS
similarity index 100%
rename from tests/native/OWNERS
rename to tests/native/connectivity_native_test/OWNERS
diff --git a/tests/native/connectivity_native_test.cpp b/tests/native/connectivity_native_test/connectivity_native_test.cpp
similarity index 100%
rename from tests/native/connectivity_native_test.cpp
rename to tests/native/connectivity_native_test/connectivity_native_test.cpp
diff --git a/tests/native/utilities/Android.bp b/tests/native/utilities/Android.bp
new file mode 100644
index 0000000..4706b3d
--- /dev/null
+++ b/tests/native/utilities/Android.bp
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test_library {
+    name: "libconnectivity_native_test_utils",
+    defaults: [
+        "netd_defaults",
+        "resolv_test_defaults"
+    ],
+    srcs: [
+        "firewall.cpp",
+    ],
+    header_libs: [
+        "bpf_connectivity_headers",
+    ],
+    export_header_lib_headers: ["bpf_connectivity_headers"],
+    export_include_dirs: ["."],
+}
diff --git a/tests/native/utilities/firewall.cpp b/tests/native/utilities/firewall.cpp
new file mode 100644
index 0000000..e4669cb
--- /dev/null
+++ b/tests/native/utilities/firewall.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "firewall.h"
+
+#include <android-base/result.h>
+#include <gtest/gtest.h>
+
+Firewall::Firewall() {
+    std::lock_guard guard(mMutex);
+    auto result = mConfigurationMap.init(CONFIGURATION_MAP_PATH);
+    EXPECT_RESULT_OK(result) << "init mConfigurationMap failed";
+
+    result = mUidOwnerMap.init(UID_OWNER_MAP_PATH);
+    EXPECT_RESULT_OK(result) << "init mUidOwnerMap failed";
+}
+
+Firewall* Firewall::getInstance() {
+    static Firewall instance;
+    return &instance;
+}
+
+Result<void> Firewall::toggleStandbyMatch(bool enable) {
+    std::lock_guard guard(mMutex);
+    uint32_t key = UID_RULES_CONFIGURATION_KEY;
+    auto oldConfiguration = mConfigurationMap.readValue(key);
+    if (!oldConfiguration.ok()) {
+        return Errorf("Cannot read the old configuration: {}", oldConfiguration.error().message());
+    }
+
+    BpfConfig newConfiguration = enable ? (oldConfiguration.value() | STANDBY_MATCH)
+                                        : (oldConfiguration.value() & (~STANDBY_MATCH));
+    auto res = mConfigurationMap.writeValue(key, newConfiguration, BPF_EXIST);
+    if (!res.ok()) return Errorf("Failed to toggle STANDBY_MATCH: {}", res.error().message());
+
+    return {};
+}
+
+Result<void> Firewall::addRule(uint32_t uid, UidOwnerMatchType match, uint32_t iif) {
+    // iif should be non-zero if and only if match == MATCH_IIF
+    if (match == IIF_MATCH && iif == 0) {
+        return Errorf("Interface match {} must have nonzero interface index", match);
+    } else if (match != IIF_MATCH && iif != 0) {
+        return Errorf("Non-interface match {} must have zero interface index", match);
+    }
+
+    std::lock_guard guard(mMutex);
+    auto oldMatch = mUidOwnerMap.readValue(uid);
+    if (oldMatch.ok()) {
+        UidOwnerValue newMatch = {
+                .iif = iif ? iif : oldMatch.value().iif,
+                .rule = static_cast<uint8_t>(oldMatch.value().rule | match),
+        };
+        auto res = mUidOwnerMap.writeValue(uid, newMatch, BPF_ANY);
+        if (!res.ok()) return Errorf("Failed to update rule: {}", res.error().message());
+    } else {
+        UidOwnerValue newMatch = {
+                .iif = iif,
+                .rule = static_cast<uint8_t>(match),
+        };
+        auto res = mUidOwnerMap.writeValue(uid, newMatch, BPF_ANY);
+        if (!res.ok()) return Errorf("Failed to add rule: {}", res.error().message());
+    }
+    return {};
+}
+
+Result<void> Firewall::removeRule(uint32_t uid, UidOwnerMatchType match) {
+    std::lock_guard guard(mMutex);
+    auto oldMatch = mUidOwnerMap.readValue(uid);
+    if (!oldMatch.ok()) return Errorf("uid: %u does not exist in map", uid);
+
+    UidOwnerValue newMatch = {
+            .iif = (match == IIF_MATCH) ? 0 : oldMatch.value().iif,
+            .rule = static_cast<uint8_t>(oldMatch.value().rule & ~match),
+    };
+    if (newMatch.rule == 0) {
+        auto res = mUidOwnerMap.deleteValue(uid);
+        if (!res.ok()) return Errorf("Failed to remove rule: {}", res.error().message());
+    } else {
+        auto res = mUidOwnerMap.writeValue(uid, newMatch, BPF_ANY);
+        if (!res.ok()) return Errorf("Failed to update rule: {}", res.error().message());
+    }
+    return {};
+}
+
+Result<void> Firewall::addUidInterfaceRules(const std::string& ifName,
+                                            const std::vector<int32_t>& uids) {
+    unsigned int iif = if_nametoindex(ifName.c_str());
+    if (!iif) return Errorf("Failed to get interface index: {}", ifName);
+
+    for (auto uid : uids) {
+        auto res = addRule(uid, IIF_MATCH, iif);
+        if (!res.ok()) return res;
+    }
+    return {};
+}
+
+Result<void> Firewall::removeUidInterfaceRules(const std::vector<int32_t>& uids) {
+    for (auto uid : uids) {
+        auto res = removeRule(uid, IIF_MATCH);
+        if (!res.ok()) return res;
+    }
+    return {};
+}
diff --git a/tests/native/utilities/firewall.h b/tests/native/utilities/firewall.h
new file mode 100644
index 0000000..185559b
--- /dev/null
+++ b/tests/native/utilities/firewall.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <bpf/BpfMap.h>
+#include <bpf_shared.h>
+
+using android::base::Result;
+using android::bpf::BpfMap;
+
+class Firewall {
+  public:
+    Firewall() EXCLUDES(mMutex);
+    static Firewall* getInstance();
+    Result<void> toggleStandbyMatch(bool enable) EXCLUDES(mMutex);
+    Result<void> addRule(uint32_t uid, UidOwnerMatchType match, uint32_t iif = 0) EXCLUDES(mMutex);
+    Result<void> removeRule(uint32_t uid, UidOwnerMatchType match) EXCLUDES(mMutex);
+    Result<void> addUidInterfaceRules(const std::string& ifName, const std::vector<int32_t>& uids);
+    Result<void> removeUidInterfaceRules(const std::vector<int32_t>& uids);
+
+  private:
+    BpfMap<uint32_t, uint32_t> mConfigurationMap GUARDED_BY(mMutex);
+    BpfMap<uint32_t, UidOwnerValue> mUidOwnerMap GUARDED_BY(mMutex);
+    std::mutex mMutex;
+};
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index ee72f85..72324d9 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -70,6 +70,7 @@
         "java/com/android/server/connectivity/NetdEventListenerServiceTest.java",
         "java/com/android/server/connectivity/VpnTest.java",
         "java/com/android/server/net/ipmemorystore/*.java",
+        "java/com/android/server/connectivity/mdns/**/*.java",
     ]
 }
 
@@ -111,8 +112,7 @@
         "service-connectivity-pre-jarjar",
         "service-connectivity-tiramisu-pre-jarjar",
         "services.core-vpn",
-        "cts-net-utils",
-        "service-mdns",
+        "cts-net-utils"
     ],
     libs: [
         "android.net.ipsec.ike.stubs.module_lib",
@@ -145,10 +145,10 @@
     static_libs: [
         "services.core",
         "services.net",
+        "service-mdns",
     ],
     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/android/net/nsd/NsdManagerTest.java b/tests/unit/java/android/net/nsd/NsdManagerTest.java
index 32274bc..e3dbb14 100644
--- a/tests/unit/java/android/net/nsd/NsdManagerTest.java
+++ b/tests/unit/java/android/net/nsd/NsdManagerTest.java
@@ -81,70 +81,70 @@
     }
 
     @Test
-    @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+    @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
     public void testResolveServiceS() throws Exception {
         verify(mServiceConn, never()).startDaemon();
         doTestResolveService();
     }
 
     @Test
-    @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+    @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
     public void testResolveServicePreS() throws Exception {
         verify(mServiceConn).startDaemon();
         doTestResolveService();
     }
 
     @Test
-    @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+    @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
     public void testDiscoverServiceS() throws Exception {
         verify(mServiceConn, never()).startDaemon();
         doTestDiscoverService();
     }
 
     @Test
-    @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+    @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
     public void testDiscoverServicePreS() throws Exception {
         verify(mServiceConn).startDaemon();
         doTestDiscoverService();
     }
 
     @Test
-    @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+    @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
     public void testParallelResolveServiceS() throws Exception {
         verify(mServiceConn, never()).startDaemon();
         doTestParallelResolveService();
     }
 
     @Test
-    @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+    @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
     public void testParallelResolveServicePreS() throws Exception {
         verify(mServiceConn).startDaemon();
         doTestParallelResolveService();
     }
 
     @Test
-    @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+    @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
     public void testInvalidCallsS() throws Exception {
         verify(mServiceConn, never()).startDaemon();
         doTestInvalidCalls();
     }
 
     @Test
-    @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+    @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
     public void testInvalidCallsPreS() throws Exception {
         verify(mServiceConn).startDaemon();
         doTestInvalidCalls();
     }
 
     @Test
-    @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+    @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
     public void testRegisterServiceS() throws Exception {
         verify(mServiceConn, never()).startDaemon();
         doTestRegisterService();
     }
 
     @Test
-    @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+    @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
     public void testRegisterServicePreS() throws Exception {
         verify(mServiceConn).startDaemon();
         doTestRegisterService();
diff --git a/tests/unit/java/com/android/server/BpfNetMapsTest.java b/tests/unit/java/com/android/server/BpfNetMapsTest.java
index 61d9eea..2d09bf2 100644
--- a/tests/unit/java/com/android/server/BpfNetMapsTest.java
+++ b/tests/unit/java/com/android/server/BpfNetMapsTest.java
@@ -46,6 +46,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
 
+import android.content.Context;
 import android.net.INetd;
 import android.os.Build;
 import android.os.ServiceSpecificException;
@@ -102,6 +103,7 @@
 
     @Mock INetd mNetd;
     @Mock BpfNetMaps.Dependencies mDeps;
+    @Mock Context mContext;
     private final BpfMap<U32, U32> mConfigurationMap = new TestBpfMap<>(U32.class, U32.class);
     private final BpfMap<U32, UidOwnerValue> mUidOwnerMap =
             new TestBpfMap<>(U32.class, UidOwnerValue.class);
@@ -110,9 +112,10 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         doReturn(TEST_IF_INDEX).when(mDeps).getIfIndex(TEST_IF_NAME);
+        BpfNetMaps.setEnableJavaBpfMapForTest(true /* enable */);
         BpfNetMaps.setConfigurationMapForTest(mConfigurationMap);
         BpfNetMaps.setUidOwnerMapForTest(mUidOwnerMap);
-        mBpfNetMaps = new BpfNetMaps(mNetd, mDeps);
+        mBpfNetMaps = new BpfNetMaps(mContext, mNetd, mDeps);
     }
 
     @Test
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 3264a36..06f7300 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -241,6 +241,7 @@
 import android.net.ConnectivityThread;
 import android.net.DataStallReportParcelable;
 import android.net.EthernetManager;
+import android.net.EthernetNetworkSpecifier;
 import android.net.IConnectivityDiagnosticsCallback;
 import android.net.IDnsResolver;
 import android.net.INetd;
@@ -428,6 +429,7 @@
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 import java.util.regex.Matcher;
@@ -922,9 +924,6 @@
         private int mProbesSucceeded;
         private String mNmValidationRedirectUrl = null;
         private boolean mNmProvNotificationRequested = false;
-        private Runnable mCreatedCallback;
-        private Runnable mUnwantedCallback;
-        private Runnable mDisconnectedCallback;
 
         private final ConditionVariable mNetworkStatusReceived = new ConditionVariable();
         // Contains the redirectUrl from networkStatus(). Before reading, wait for
@@ -932,22 +931,34 @@
         private String mRedirectUrl;
 
         TestNetworkAgentWrapper(int transport) throws Exception {
-            this(transport, new LinkProperties(), null /* ncTemplate */, null /* provider */);
+            this(transport, new LinkProperties(), null /* ncTemplate */, null /* provider */, null);
         }
 
         TestNetworkAgentWrapper(int transport, LinkProperties linkProperties)
                 throws Exception {
-            this(transport, linkProperties, null /* ncTemplate */, null /* provider */);
+            this(transport, linkProperties, null /* ncTemplate */, null /* provider */, null);
         }
 
         private TestNetworkAgentWrapper(int transport, LinkProperties linkProperties,
                 NetworkCapabilities ncTemplate) throws Exception {
-            this(transport, linkProperties, ncTemplate, null /* provider */);
+            this(transport, linkProperties, ncTemplate, null /* provider */, null);
         }
 
         private TestNetworkAgentWrapper(int transport, LinkProperties linkProperties,
                 NetworkCapabilities ncTemplate, NetworkProvider provider) throws Exception {
-            super(transport, linkProperties, ncTemplate, provider, mServiceContext);
+            this(transport, linkProperties, ncTemplate, provider /* provider */, null);
+        }
+
+        private TestNetworkAgentWrapper(int transport, NetworkAgentWrapper.Callbacks callbacks)
+                throws Exception {
+            this(transport, new LinkProperties(), null /* ncTemplate */, null /* provider */,
+                    callbacks);
+        }
+
+        private TestNetworkAgentWrapper(int transport, LinkProperties linkProperties,
+                NetworkCapabilities ncTemplate, NetworkProvider provider,
+                NetworkAgentWrapper.Callbacks callbacks) throws Exception {
+            super(transport, linkProperties, ncTemplate, provider, callbacks, mServiceContext);
 
             // Waits for the NetworkAgent to be registered, which includes the creation of the
             // NetworkMonitor.
@@ -968,23 +979,6 @@
                 mNetworkStatusReceived.open();
             }
 
-            @Override
-            public void onNetworkCreated() {
-                super.onNetworkCreated();
-                if (mCreatedCallback != null) mCreatedCallback.run();
-            }
-
-            @Override
-            public void onNetworkUnwanted() {
-                super.onNetworkUnwanted();
-                if (mUnwantedCallback != null) mUnwantedCallback.run();
-            }
-
-            @Override
-            public void onNetworkDestroyed() {
-                super.onNetworkDestroyed();
-                if (mDisconnectedCallback != null) mDisconnectedCallback.run();
-            }
         }
 
         @Override
@@ -1214,18 +1208,6 @@
             p.timestampMillis = DATA_STALL_TIMESTAMP;
             mNmCallbacks.notifyDataStallSuspected(p);
         }
-
-        public void setCreatedCallback(Runnable r) {
-            mCreatedCallback = r;
-        }
-
-        public void setUnwantedCallback(Runnable r) {
-            mUnwantedCallback = r;
-        }
-
-        public void setDisconnectedCallback(Runnable r) {
-            mDisconnectedCallback = r;
-        }
     }
 
     /**
@@ -1854,6 +1836,7 @@
         // getSystemService() correctly.
         mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService);
         mService.systemReadyInternal();
+        verify(mMockDnsResolver).registerUnsolicitedEventListener(any());
         mVpnManagerService = makeVpnManagerService();
         mVpnManagerService.systemReady();
         mockVpn(Process.myUid());
@@ -2061,7 +2044,7 @@
         }
 
         @Override
-        public BpfNetMaps getBpfNetMaps(INetd netd) {
+        public BpfNetMaps getBpfNetMaps(Context context, INetd netd) {
             return mBpfNetMaps;
         }
 
@@ -2604,7 +2587,7 @@
         doTestValidatedCellularOutscoresUnvalidatedWiFi(false);
     }
 
-    public void doTestValidatedCellularOutscoresUnvalidatedWiFi(
+    private void doTestValidatedCellularOutscoresUnvalidatedWiFi(
             final boolean cellRadioTimesharingCapable) throws Exception {
         mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
         // Test bringing up unvalidated WiFi
@@ -2652,7 +2635,7 @@
         doTestUnvalidatedWifiOutscoresUnvalidatedCellular(false);
     }
 
-    public void doTestUnvalidatedWifiOutscoresUnvalidatedCellular(
+    private void doTestUnvalidatedWifiOutscoresUnvalidatedCellular(
             final boolean cellRadioTimesharingCapable) throws Exception {
         mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
         // Test bringing up unvalidated cellular.
@@ -2691,7 +2674,7 @@
         doTestUnlingeringDoesNotValidate(false);
     }
 
-    public void doTestUnlingeringDoesNotValidate(
+    private void doTestUnlingeringDoesNotValidate(
             final boolean cellRadioTimesharingCapable) throws Exception {
         mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
         // Test bringing up unvalidated WiFi.
@@ -2740,7 +2723,7 @@
         doTestRequestMigrationToSameTransport(TRANSPORT_ETHERNET, true);
     }
 
-    public void doTestRequestMigrationToSameTransport(final int transport,
+    private void doTestRequestMigrationToSameTransport(final int transport,
             final boolean expectLingering) throws Exception {
         // To speed up tests the linger delay is very short by default in tests but this
         // test needs to make sure the delay is not incurred so a longer value is safer (it
@@ -2845,7 +2828,7 @@
         doTestCellularOutscoresWeakWifi(false);
     }
 
-    public void doTestCellularOutscoresWeakWifi(
+    private void doTestCellularOutscoresWeakWifi(
             final boolean cellRadioTimesharingCapable) throws Exception {
         mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
         // Test bringing up validated cellular.
@@ -2884,7 +2867,7 @@
         doTestReapingNetwork(false);
     }
 
-    public void doTestReapingNetwork(
+    private void doTestReapingNetwork(
             final boolean cellRadioTimesharingCapable) throws Exception {
         mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
         // Test bringing up WiFi without NET_CAPABILITY_INTERNET.
@@ -2926,7 +2909,7 @@
         doTestCellularFallback(false);
     }
 
-    public void doTestCellularFallback(
+    private void doTestCellularFallback(
             final boolean cellRadioTimesharingCapable) throws Exception {
         mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
         // Test bringing up validated cellular.
@@ -2977,7 +2960,7 @@
         doTestWiFiFallback(false);
     }
 
-    public void doTestWiFiFallback(
+    private void doTestWiFiFallback(
             final boolean cellRadioTimesharingCapable) throws Exception {
         mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
         // Test bringing up unvalidated WiFi.
@@ -3565,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();
             }
@@ -3603,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
@@ -3633,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);
@@ -7256,9 +7242,6 @@
     public void testBasicDnsConfigurationPushed() throws Exception {
         setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
 
-        // Clear any interactions that occur as a result of CS starting up.
-        reset(mMockDnsResolver);
-
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         waitForIdle();
         verify(mMockDnsResolver, never()).setResolverConfiguration(any());
@@ -7331,9 +7314,6 @@
 
     @Test
     public void testDnsConfigurationTransTypesPushed() throws Exception {
-        // Clear any interactions that occur as a result of CS starting up.
-        reset(mMockDnsResolver);
-
         final NetworkRequest request = new NetworkRequest.Builder()
                 .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET)
                 .build();
@@ -7392,9 +7372,6 @@
 
     @Test
     public void testPrivateDnsSettingsChange() throws Exception {
-        // Clear any interactions that occur as a result of CS starting up.
-        reset(mMockDnsResolver);
-
         // The default on Android is opportunistic mode ("Automatic").
         setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
 
@@ -8555,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.
@@ -8588,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();
     }
 
@@ -9047,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>();
@@ -9080,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();
@@ -9095,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();
@@ -9134,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);
@@ -9154,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();
@@ -9168,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();
@@ -9182,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();
@@ -9196,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();
@@ -9842,8 +9811,6 @@
         cellLp.addRoute(ipv6Default);
         cellLp.addRoute(ipv6Subnet);
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
-        reset(mMockDnsResolver);
-        reset(mMockNetd);
         reset(mClatCoordinator);
 
         // Connect with ipv6 link properties. Expect prefix discovery to be started.
@@ -14360,7 +14327,7 @@
      * Make sure per profile network preferences behave as expected for a given
      * profile network preference.
      */
-    public void testPreferenceForUserNetworkUpDownForGivenPreference(
+    private void doTestPreferenceForUserNetworkUpDownForGivenPreference(
             ProfileNetworkPreference profileNetworkPreference,
             boolean connectWorkProfileAgentAhead,
             UserHandle testHandle,
@@ -14604,7 +14571,7 @@
                 new ProfileNetworkPreference.Builder();
         profileNetworkPreferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
         profileNetworkPreferenceBuilder.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
-        testPreferenceForUserNetworkUpDownForGivenPreference(
+        doTestPreferenceForUserNetworkUpDownForGivenPreference(
                 profileNetworkPreferenceBuilder.build(), false,
                 testHandle, mProfileDefaultNetworkCallback, null);
     }
@@ -14623,7 +14590,7 @@
                 PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK);
         profileNetworkPreferenceBuilder.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
         registerDefaultNetworkCallbacks();
-        testPreferenceForUserNetworkUpDownForGivenPreference(
+        doTestPreferenceForUserNetworkUpDownForGivenPreference(
                 profileNetworkPreferenceBuilder.build(), false,
                 testHandle, mProfileDefaultNetworkCallback, null);
     }
@@ -14644,7 +14611,7 @@
                 PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK);
         profileNetworkPreferenceBuilder.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
         registerDefaultNetworkCallbacks();
-        testPreferenceForUserNetworkUpDownForGivenPreference(
+        doTestPreferenceForUserNetworkUpDownForGivenPreference(
                 profileNetworkPreferenceBuilder.build(), true, testHandle,
                 mProfileDefaultNetworkCallback, null);
     }
@@ -14663,7 +14630,7 @@
         profileNetworkPreferenceBuilder.setIncludedUids(
                 new int[]{testHandle.getUid(TEST_WORK_PROFILE_APP_UID)});
         registerDefaultNetworkCallbacks();
-        testPreferenceForUserNetworkUpDownForGivenPreference(
+        doTestPreferenceForUserNetworkUpDownForGivenPreference(
                 profileNetworkPreferenceBuilder.build(), false, testHandle,
                 mProfileDefaultNetworkCallback, null);
     }
@@ -14682,7 +14649,7 @@
         profileNetworkPreferenceBuilder.setIncludedUids(
                 new int[]{testHandle.getUid(TEST_WORK_PROFILE_APP_UID_2)});
         registerDefaultNetworkCallbacks();
-        testPreferenceForUserNetworkUpDownForGivenPreference(
+        doTestPreferenceForUserNetworkUpDownForGivenPreference(
                 profileNetworkPreferenceBuilder.build(), false,
                 testHandle, mProfileDefaultNetworkCallbackAsAppUid2, null);
     }
@@ -14701,7 +14668,7 @@
         profileNetworkPreferenceBuilder.setExcludedUids(
                 new int[]{testHandle.getUid(TEST_WORK_PROFILE_APP_UID_2)});
         registerDefaultNetworkCallbacks();
-        testPreferenceForUserNetworkUpDownForGivenPreference(
+        doTestPreferenceForUserNetworkUpDownForGivenPreference(
                 profileNetworkPreferenceBuilder.build(), false,
                 testHandle, mProfileDefaultNetworkCallback,
                 mProfileDefaultNetworkCallbackAsAppUid2);
@@ -14797,7 +14764,7 @@
         profileNetworkPreferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
         profileNetworkPreferenceBuilder.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
         registerDefaultNetworkCallbacks();
-        testPreferenceForUserNetworkUpDownForGivenPreference(
+        doTestPreferenceForUserNetworkUpDownForGivenPreference(
                 profileNetworkPreferenceBuilder.build(), true,
                 testHandle, mProfileDefaultNetworkCallback,
                 null);
@@ -14817,7 +14784,7 @@
                 PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK);
         profileNetworkPreferenceBuilder.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
         registerDefaultNetworkCallbacks();
-        testPreferenceForUserNetworkUpDownForGivenPreference(
+        doTestPreferenceForUserNetworkUpDownForGivenPreference(
                 profileNetworkPreferenceBuilder.build(), true,
                 testHandle, mProfileDefaultNetworkCallback,
                 null);
@@ -14838,7 +14805,7 @@
         profileNetworkPreferenceBuilder.setPreferenceEnterpriseId(
                 NET_ENTERPRISE_ID_2);
         registerDefaultNetworkCallbacks();
-        testPreferenceForUserNetworkUpDownForGivenPreference(
+        doTestPreferenceForUserNetworkUpDownForGivenPreference(
                 profileNetworkPreferenceBuilder.build(), true,
                 testHandle, mProfileDefaultNetworkCallback, null);
     }
@@ -15733,6 +15700,45 @@
         mCm.unregisterNetworkCallback(cb);
     }
 
+    @Test
+    public void testSanitizedCapabilitiesFromAgentDoesNotMutateArgument()
+            throws Exception {
+        // This NetworkCapabilities builds an usual object to maximize the chance that this requires
+        // sanitization, so we have a high chance to detect any changes to the original.
+        final NetworkCapabilities unsanitized = new NetworkCapabilities.Builder()
+                .withoutDefaultCapabilities()
+                .addTransportType(TRANSPORT_WIFI)
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .setOwnerUid(12345)
+                .setAdministratorUids(new int[] {12345, 23456, 34567})
+                .setLinkUpstreamBandwidthKbps(20)
+                .setLinkDownstreamBandwidthKbps(10)
+                .setNetworkSpecifier(new EthernetNetworkSpecifier("foobar"))
+                .setTransportInfo(new WifiInfo.Builder().setBssid("AA:AA:AA:AA:AA:AA").build())
+                .setSignalStrength(-75)
+                .setSsid("SSID1")
+                .setRequestorUid(98765)
+                .setRequestorPackageName("TestPackage")
+                .setSubscriptionIds(Collections.singleton(Process.myUid()))
+                .setUids(UidRange.toIntRanges(uidRangesForUids(
+                        UserHandle.getUid(PRIMARY_USER, 10100),
+                        UserHandle.getUid(SECONDARY_USER, 10101),
+                        UserHandle.getUid(TERTIARY_USER, 10043))))
+                .setAllowedUids(Set.of(45678, 56789, 65432))
+                .setUnderlyingNetworks(List.of(new Network(99999)))
+                .build();
+        final NetworkCapabilities copyOfUnsanitized = new NetworkCapabilities(unsanitized);
+        final NetworkInfo info = new NetworkInfo(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_LTE,
+                ConnectivityManager.getNetworkTypeName(TYPE_MOBILE),
+                TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_LTE));
+        final NetworkAgentInfo agent = fakeNai(unsanitized, info);
+        agent.setDeclaredCapabilities(unsanitized);
+        final NetworkCapabilities sanitized = agent.getDeclaredCapabilitiesSanitized(
+                null /* carrierPrivilegeAuthenticator */);
+        assertEquals(copyOfUnsanitized, unsanitized);
+        assertNotEquals(sanitized, unsanitized);
+    }
+
     /**
      * Validate request counts are counted accurately on setProfileNetworkPreference on set/replace.
      */
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index 07884cf..1813393 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -159,7 +159,7 @@
     }
 
     @Test
-    @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+    @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
     public void testPreSClients() throws Exception {
         // Pre S client connected, the daemon should be started.
         connectClient(mService);
@@ -186,7 +186,7 @@
     }
 
     @Test
-    @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+    @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
     public void testNoDaemonStartedWhenClientsConnect() throws Exception {
         // Creating an NsdManager will not cause daemon startup.
         connectClient(mService);
@@ -220,7 +220,7 @@
     }
 
     @Test
-    @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+    @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
     public void testClientRequestsAreGCedAtDisconnection() throws Exception {
         final NsdManager client = connectClient(mService);
         final INsdManagerCallback cb1 = getCallback();
@@ -263,7 +263,7 @@
     }
 
     @Test
-    @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+    @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
     public void testCleanupDelayNoRequestActive() throws Exception {
         final NsdManager client = connectClient(mService);
 
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/FullScoreTest.kt b/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt
index c03a9cd..a194131 100644
--- a/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt
@@ -18,6 +18,8 @@
 
 import android.net.NetworkAgentConfig
 import android.net.NetworkCapabilities
+import android.net.NetworkScore
+import android.net.NetworkScore.KEEP_CONNECTED_FOR_HANDOVER
 import android.net.NetworkScore.KEEP_CONNECTED_NONE
 import android.os.Build
 import android.text.TextUtils
@@ -25,6 +27,7 @@
 import android.util.Log
 import androidx.test.filters.SmallTest
 import com.android.server.connectivity.FullScore.MAX_CS_MANAGED_POLICY
+import com.android.server.connectivity.FullScore.MIN_CS_MANAGED_POLICY
 import com.android.server.connectivity.FullScore.POLICY_ACCEPT_UNVALIDATED
 import com.android.server.connectivity.FullScore.POLICY_EVER_USER_SELECTED
 import com.android.server.connectivity.FullScore.POLICY_IS_DESTROYED
@@ -40,6 +43,7 @@
 import kotlin.reflect.full.staticProperties
 import kotlin.test.assertEquals
 import kotlin.test.assertFalse
+import kotlin.test.assertNotEquals
 import kotlin.test.assertTrue
 
 @RunWith(DevSdkIgnoreRunner::class)
@@ -83,33 +87,10 @@
     }
 
     @Test
-    fun testGetLegacyInt() {
-        val ns = FullScore(50, 0L /* policy */, KEEP_CONNECTED_NONE)
-        assertEquals(10, ns.legacyInt) // -40 penalty for not being validated
-        assertEquals(50, ns.legacyIntAsValidated)
-
-        val vpnNs = FullScore(101, 0L /* policy */, KEEP_CONNECTED_NONE).withPolicies(vpn = true)
-        assertEquals(101, vpnNs.legacyInt) // VPNs are not subject to unvalidation penalty
-        assertEquals(101, vpnNs.legacyIntAsValidated)
-        assertEquals(101, vpnNs.withPolicies(validated = true).legacyInt)
-        assertEquals(101, vpnNs.withPolicies(validated = true).legacyIntAsValidated)
-
-        val validatedNs = ns.withPolicies(validated = true)
-        assertEquals(50, validatedNs.legacyInt) // No penalty, this is validated
-        assertEquals(50, validatedNs.legacyIntAsValidated)
-
-        val chosenNs = ns.withPolicies(onceChosen = true)
-        assertEquals(10, chosenNs.legacyInt)
-        assertEquals(100, chosenNs.legacyIntAsValidated)
-        assertEquals(10, chosenNs.withPolicies(acceptUnvalidated = true).legacyInt)
-        assertEquals(50, chosenNs.withPolicies(acceptUnvalidated = true).legacyIntAsValidated)
-    }
-
-    @Test
     fun testToString() {
-        val string = FullScore(10, 0L /* policy */, KEEP_CONNECTED_NONE)
+        val string = FullScore(0L /* policy */, KEEP_CONNECTED_NONE)
                 .withPolicies(vpn = true, acceptUnvalidated = true).toString()
-        assertTrue(string.contains("Score(10"), string)
+        assertTrue(string.contains("Score("), string)
         assertTrue(string.contains("ACCEPT_UNVALIDATED"), string)
         assertTrue(string.contains("IS_VPN"), string)
         assertFalse(string.contains("IS_VALIDATED"), string)
@@ -131,7 +112,7 @@
 
     @Test
     fun testHasPolicy() {
-        val ns = FullScore(50, 0L /* policy */, KEEP_CONNECTED_NONE)
+        val ns = FullScore(0L /* policy */, KEEP_CONNECTED_NONE)
         assertFalse(ns.hasPolicy(POLICY_IS_VALIDATED))
         assertFalse(ns.hasPolicy(POLICY_IS_VPN))
         assertFalse(ns.hasPolicy(POLICY_EVER_USER_SELECTED))
@@ -148,12 +129,23 @@
         val policies = getAllPolicies()
 
         policies.forEach { policy ->
-            assertTrue(policy.get() as Int >= FullScore.MIN_CS_MANAGED_POLICY)
-            assertTrue(policy.get() as Int <= FullScore.MAX_CS_MANAGED_POLICY)
+            assertTrue(policy.get() as Int >= MIN_CS_MANAGED_POLICY)
+            assertTrue(policy.get() as Int <= MAX_CS_MANAGED_POLICY)
         }
-        assertEquals(FullScore.MIN_CS_MANAGED_POLICY,
-                policies.minOfOrNull { it.get() as Int })
-        assertEquals(FullScore.MAX_CS_MANAGED_POLICY,
-                policies.maxOfOrNull { it.get() as Int })
+        assertEquals(MIN_CS_MANAGED_POLICY, policies.minOfOrNull { it.get() as Int })
+        assertEquals(MAX_CS_MANAGED_POLICY, policies.maxOfOrNull { it.get() as Int })
+    }
+
+    @Test
+    fun testEquals() {
+        val ns1 = FullScore(0L /* policy */, KEEP_CONNECTED_NONE)
+        val ns2 = FullScore(0L /* policy */, KEEP_CONNECTED_NONE)
+        val ns3 = FullScore(0L /* policy */, KEEP_CONNECTED_FOR_HANDOVER)
+        val ns4 = NetworkScore.Builder().setLegacyInt(50).build()
+        assertEquals(ns1, ns1)
+        assertEquals(ns2, ns1)
+        assertNotEquals(ns1.withPolicies(validated = true), ns1)
+        assertNotEquals(ns3, ns1)
+        assertFalse(ns1.equals(ns4))
     }
 }
diff --git a/tests/unit/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/unit/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 063ccd3..ad8613f 100644
--- a/tests/unit/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/unit/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -138,18 +138,16 @@
     private void logDefaultNetworkEvent(long timeMs, NetworkAgentInfo nai,
             NetworkAgentInfo oldNai) {
         final Network network = (nai != null) ? nai.network() : null;
-        final int score = (nai != null) ? nai.getCurrentScore() : 0;
         final boolean validated = (nai != null) ? nai.lastValidated : false;
         final LinkProperties lp = (nai != null) ? nai.linkProperties : null;
         final NetworkCapabilities nc = (nai != null) ? nai.networkCapabilities : null;
 
         final Network prevNetwork = (oldNai != null) ? oldNai.network() : null;
-        final int prevScore = (oldNai != null) ? oldNai.getCurrentScore() : 0;
         final LinkProperties prevLp = (oldNai != null) ? oldNai.linkProperties : null;
         final NetworkCapabilities prevNc = (oldNai != null) ? oldNai.networkCapabilities : null;
 
-        mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, network, score, validated,
-                lp, nc, prevNetwork, prevScore, prevLp, prevNc);
+        mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, network, 0 /* legacyScore */,
+                validated, lp, nc, prevNetwork, 0 /* prevLegacyScore */, prevLp, prevNc);
     }
     @Test
     public void testDefaultNetworkEvents() throws Exception {
@@ -158,15 +156,15 @@
 
         NetworkAgentInfo[][] defaultNetworks = {
             // nothing -> cell
-            {null, makeNai(100, 10, false, true, cell)},
+            {null, makeNai(100, false, true, cell)},
             // cell -> wifi
-            {makeNai(100, 50, true, true, cell), makeNai(101, 20, true, false, wifi)},
+            {makeNai(100, true, true, cell), makeNai(101, true, false, wifi)},
             // wifi -> nothing
-            {makeNai(101, 60, true, false, wifi), null},
+            {makeNai(101, true, false, wifi), null},
             // nothing -> cell
-            {null, makeNai(102, 10, true, true, cell)},
+            {null, makeNai(102, true, true, cell)},
             // cell -> wifi
-            {makeNai(102, 50, true, true, cell), makeNai(103, 20, true, false, wifi)},
+            {makeNai(102, true, true, cell), makeNai(103, true, false, wifi)},
         };
 
         long timeMs = mService.mDefaultNetworkMetrics.creationTimeMs;
@@ -204,8 +202,8 @@
                 "  transports: 1",
                 "  default_network_event <",
                 "    default_network_duration_ms: 2002",
-                "    final_score: 50",
-                "    initial_score: 10",
+                "    final_score: 0",
+                "    initial_score: 0",
                 "    ip_support: 3",
                 "    no_default_network_duration_ms: 0",
                 "    previous_default_network_link_layer: 0",
@@ -221,8 +219,8 @@
                 "  transports: 2",
                 "  default_network_event <",
                 "    default_network_duration_ms: 4004",
-                "    final_score: 60",
-                "    initial_score: 20",
+                "    final_score: 0",
+                "    initial_score: 0",
                 "    ip_support: 1",
                 "    no_default_network_duration_ms: 0",
                 "    previous_default_network_link_layer: 2",
@@ -255,8 +253,8 @@
                 "  transports: 1",
                 "  default_network_event <",
                 "    default_network_duration_ms: 16016",
-                "    final_score: 50",
-                "    initial_score: 10",
+                "    final_score: 0",
+                "    initial_score: 0",
                 "    ip_support: 3",
                 "    no_default_network_duration_ms: 0",
                 "    previous_default_network_link_layer: 4",
@@ -348,8 +346,8 @@
         long timeMs = mService.mDefaultNetworkMetrics.creationTimeMs;
         final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
         final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI});
-        NetworkAgentInfo cellNai = makeNai(100, 50, false, true, cell);
-        NetworkAgentInfo wifiNai = makeNai(101, 60, true, false, wifi);
+        final NetworkAgentInfo cellNai = makeNai(100, false, true, cell);
+        final NetworkAgentInfo wifiNai = makeNai(101, true, false, wifi);
         logDefaultNetworkEvent(timeMs + 200L, cellNai, null);
         logDefaultNetworkEvent(timeMs + 300L, wifiNai, cellNai);
 
@@ -463,8 +461,8 @@
                 "  transports: 1",
                 "  default_network_event <",
                 "    default_network_duration_ms: 100",
-                "    final_score: 50",
-                "    initial_score: 50",
+                "    final_score: 0",
+                "    initial_score: 0",
                 "    ip_support: 2",
                 "    no_default_network_duration_ms: 0",
                 "    previous_default_network_link_layer: 0",
@@ -611,10 +609,9 @@
         mNetdListener.onWakeupEvent(prefix, uid, ether, ip, mac, srcIp, dstIp, sport, dport, now);
     }
 
-    NetworkAgentInfo makeNai(int netId, int score, boolean ipv4, boolean ipv6, long transports) {
+    NetworkAgentInfo makeNai(int netId, boolean ipv4, boolean ipv6, long transports) {
         NetworkAgentInfo nai = mock(NetworkAgentInfo.class);
         when(nai.network()).thenReturn(new Network(netId));
-        when(nai.getCurrentScore()).thenReturn(score);
         nai.linkProperties = new LinkProperties();
         nai.networkCapabilities = new NetworkCapabilities();
         nai.lastValidated = true;
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/NetworkOfferTest.kt b/tests/unit/java/com/android/server/connectivity/NetworkOfferTest.kt
index d03c567..f9a0927 100644
--- a/tests/unit/java/com/android/server/connectivity/NetworkOfferTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/NetworkOfferTest.kt
@@ -42,7 +42,7 @@
 
     @Test
     fun testOfferNeededUnneeded() {
-        val score = FullScore(50, POLICY_NONE, KEEP_CONNECTED_NONE)
+        val score = FullScore(POLICY_NONE, KEEP_CONNECTED_NONE)
         val offer = NetworkOffer(score, NetworkCapabilities.Builder().build(), mockCallback,
                 1 /* providerId */)
         val request1 = mock(NetworkRequest::class.java)
diff --git a/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt b/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt
index 4408958..6f9f430 100644
--- a/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt
@@ -33,7 +33,7 @@
 import org.junit.runner.RunWith
 import kotlin.test.assertEquals
 
-private fun score(vararg policies: Int) = FullScore(0,
+private fun score(vararg policies: Int) = FullScore(
         policies.fold(0L) { acc, e -> acc or (1L shl e) }, KEEP_CONNECTED_NONE)
 private fun caps(transport: Int) = NetworkCapabilities.Builder().addTransportType(transport).build()
 
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/NetworkStatsFactoryTest.java b/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
index f6fb45c..14455fa 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -33,23 +33,26 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
 
 import android.content.Context;
-import android.content.res.Resources;
 import android.net.NetworkStats;
 import android.net.TrafficStats;
 import android.net.UnderlyingNetworkInfo;
+import android.os.SystemClock;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import com.android.frameworks.tests.net.R;
+import com.android.internal.util.ProcFileReader;
 import com.android.server.BpfNetMaps;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRunner;
 
 import libcore.io.IoUtils;
-import libcore.io.Streams;
 import libcore.testing.io.TestIoUtils;
 
 import org.junit.After;
@@ -60,10 +63,8 @@
 import org.mockito.MockitoAnnotations;
 
 import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.io.InputStream;
-import java.io.OutputStream;
+import java.io.IOException;
+import java.net.ProtocolException;
 
 /** Tests for {@link NetworkStatsFactory}. */
 @RunWith(DevSdkIgnoreRunner.class)
@@ -75,6 +76,7 @@
     private File mTestProc;
     private NetworkStatsFactory mFactory;
     @Mock private Context mContext;
+    @Mock private NetworkStatsFactory.Dependencies mDeps;
     @Mock private BpfNetMaps mBpfNetMaps;
 
     @Before
@@ -86,7 +88,8 @@
         // applications. So in order to have a test support native library, the native code
         // related to networkStatsFactory is compiled to a minimal native library and loaded here.
         System.loadLibrary("networkstatsfactorytestjni");
-        mFactory = new NetworkStatsFactory(mContext, mTestProc, false, mBpfNetMaps);
+        doReturn(mBpfNetMaps).when(mDeps).createBpfNetMaps(any());
+        mFactory = new NetworkStatsFactory(mContext, mDeps);
         mFactory.updateUnderlyingNetworkInfos(new UnderlyingNetworkInfo[0]);
     }
 
@@ -97,7 +100,7 @@
 
     @Test
     public void testNetworkStatsDetail() throws Exception {
-        final NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_typical);
+        final NetworkStats stats = factoryReadNetworkStatsDetail(R.raw.xt_qtaguid_typical);
 
         assertEquals(70, stats.size());
         assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 18621L, 2898L);
@@ -122,8 +125,8 @@
         // over VPN.
         //
         // VPN UID rewrites packets read from TUN back to TUN, plus some of its own traffic
-
-        final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_rewrite_through_self);
+        final NetworkStats tunStats =
+                factoryReadNetworkStatsDetail(R.raw.xt_qtaguid_vpn_rewrite_through_self);
 
         assertValues(tunStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
                 DEFAULT_NETWORK_ALL, 2000L, 200L, 1000L, 100L, 0);
@@ -164,7 +167,8 @@
         // UID_RED: 2000 bytes, 200 packets
         // UID_BLUE: 1000 bytes, 100 packets
         // UID_VPN: 6300 bytes, 0 packets
-        final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_with_clat);
+        final NetworkStats tunStats =
+                factoryReadNetworkStatsDetail(R.raw.xt_qtaguid_vpn_with_clat);
 
         assertValues(tunStats, CLAT_PREFIX + TEST_IFACE, UID_RED, 2000L, 200L, 1000, 100L);
         assertValues(tunStats, CLAT_PREFIX + TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L);
@@ -188,7 +192,8 @@
         // attributed to UID_BLUE, and 150 bytes attributed to UID_VPN.
         // Of 3300 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes
         // attributed to UID_BLUE, and 300 bytes attributed to UID_VPN.
-        final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying);
+        final NetworkStats tunStats =
+                factoryReadNetworkStatsDetail(R.raw.xt_qtaguid_vpn_one_underlying);
 
         assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L);
         assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L);
@@ -217,7 +222,7 @@
         // Of 8800 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes
         // attributed to UID_BLUE, and 5800 bytes attributed to UID_VPN.
         final NetworkStats tunStats =
-                parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying_own_traffic);
+                factoryReadNetworkStatsDetail(R.raw.xt_qtaguid_vpn_one_underlying_own_traffic);
 
         assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L);
         assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L);
@@ -239,7 +244,7 @@
         // Of 1000 bytes over WiFi, expect 250 bytes attributed UID_RED and 750 bytes to UID_BLUE,
         // with nothing attributed to UID_VPN for both rx/tx traffic.
         final NetworkStats tunStats =
-                parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying_compression);
+                factoryReadNetworkStatsDetail(R.raw.xt_qtaguid_vpn_one_underlying_compression);
 
         assertValues(tunStats, TEST_IFACE, UID_RED, 250L, 25L, 250L, 25L);
         assertValues(tunStats, TEST_IFACE, UID_BLUE, 750L, 75L, 750L, 75L);
@@ -263,7 +268,7 @@
         // - 500 bytes rx/tx each over WiFi/Cell attributed to both UID_RED and UID_BLUE.
         // - 1200 bytes rx/tx each over WiFi/Cell for VPN_UID.
         final NetworkStats tunStats =
-                parseDetailedStats(R.raw.xt_qtaguid_vpn_two_underlying_duplication);
+                factoryReadNetworkStatsDetail(R.raw.xt_qtaguid_vpn_two_underlying_duplication);
 
         assertValues(tunStats, TEST_IFACE, UID_RED, 500L, 50L, 500L, 50L);
         assertValues(tunStats, TEST_IFACE, UID_BLUE, 500L, 50L, 500L, 50L);
@@ -303,7 +308,7 @@
         // Of 3850 bytes received over Cell, expect 3000 bytes attributed to UID_RED, 500 bytes
         // attributed to UID_BLUE, and 350 bytes attributed to UID_VPN.
         final NetworkStats tunStats =
-                parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying_two_vpn);
+                factoryReadNetworkStatsDetail(R.raw.xt_qtaguid_vpn_one_underlying_two_vpn);
 
         assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L);
         assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L);
@@ -333,7 +338,8 @@
         //
         // For UID_VPN, expect 60 bytes attributed over WiFi and 40 bytes over Cell for tx traffic.
         // And, 30 bytes over WiFi and 20 bytes over Cell for rx traffic.
-        final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_two_underlying_split);
+        final NetworkStats tunStats =
+                factoryReadNetworkStatsDetail(R.raw.xt_qtaguid_vpn_two_underlying_split);
 
         assertValues(tunStats, TEST_IFACE, UID_RED, 300L, 30L, 600L, 60L);
         assertValues(tunStats, TEST_IFACE, UID_VPN, 30L, 0L, 60L, 0L);
@@ -357,7 +363,8 @@
         // rx/tx.
         // UID_VPN gets nothing attributed to it (avoiding negative stats).
         final NetworkStats tunStats =
-                parseDetailedStats(R.raw.xt_qtaguid_vpn_two_underlying_split_compression);
+                factoryReadNetworkStatsDetail(
+                        R.raw.xt_qtaguid_vpn_two_underlying_split_compression);
 
         assertValues(tunStats, TEST_IFACE, UID_RED, 600L, 60L, 600L, 60L);
         assertValues(tunStats, TEST_IFACE, UID_VPN, 0L, 0L, 0L, 0L);
@@ -378,7 +385,8 @@
         // 1000 bytes (100 packets) were sent/received by UID_RED over VPN.
         // VPN sent/received 1100 bytes (100 packets) over Cell.
         // Of 1100 bytes over Cell, expect all of it attributed to UID_VPN for both rx/tx traffic.
-        final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_incorrect_iface);
+        final NetworkStats tunStats =
+                factoryReadNetworkStatsDetail(R.raw.xt_qtaguid_vpn_incorrect_iface);
 
         assertValues(tunStats, TEST_IFACE, UID_RED, 0L, 0L, 0L, 0L);
         assertValues(tunStats, TEST_IFACE, UID_VPN, 0L, 0L, 0L, 0L);
@@ -403,7 +411,9 @@
 
     @Test
     public void testNetworkStatsWithSet() throws Exception {
-        final NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_typical);
+        final NetworkStats stats =
+                factoryReadNetworkStatsDetail(R.raw.xt_qtaguid_typical);
+
         assertEquals(70, stats.size());
         assertStatsEntry(stats, "rmnet1", 10021, SET_DEFAULT, 0x30100000, 219110L, 578L, 227423L,
                 676L);
@@ -411,29 +421,6 @@
     }
 
     @Test
-    public void testNetworkStatsSingle() throws Exception {
-        stageFile(R.raw.xt_qtaguid_iface_typical, file("net/xt_qtaguid/iface_stat_all"));
-
-        final NetworkStats stats = mFactory.readNetworkStatsSummaryDev();
-        assertEquals(6, stats.size());
-        assertStatsEntry(stats, "rmnet0", UID_ALL, SET_ALL, TAG_NONE, 2112L, 24L, 700L, 10L);
-        assertStatsEntry(stats, "test1", UID_ALL, SET_ALL, TAG_NONE, 6L, 8L, 10L, 12L);
-        assertStatsEntry(stats, "test2", UID_ALL, SET_ALL, TAG_NONE, 1L, 2L, 3L, 4L);
-    }
-
-    @Test
-    public void testNetworkStatsXt() throws Exception {
-        stageFile(R.raw.xt_qtaguid_iface_fmt_typical, file("net/xt_qtaguid/iface_stat_fmt"));
-
-        final NetworkStats stats = mFactory.readNetworkStatsSummaryXt();
-        assertEquals(3, stats.size());
-        assertStatsEntry(stats, "rmnet0", UID_ALL, SET_ALL, TAG_NONE, 6824L, 16L, 5692L, 10L);
-        assertStatsEntry(stats, "rmnet1", UID_ALL, SET_ALL, TAG_NONE, 11153922L, 8051L, 190226L,
-                2468L);
-        assertStatsEntry(stats, "rmnet2", UID_ALL, SET_ALL, TAG_NONE, 4968L, 35L, 3081L, 39L);
-    }
-
-    @Test
     public void testDoubleClatAccountingSimple() throws Exception {
         mFactory.noteStackedIface("v4-wlan0", "wlan0");
 
@@ -441,7 +428,8 @@
         //  - 213 received 464xlat packets of size 200 bytes
         //  - 41 sent 464xlat packets of size 100 bytes
         //  - no other traffic on base interface for root uid.
-        NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_simple);
+        final NetworkStats stats =
+                factoryReadNetworkStatsDetail(R.raw.xt_qtaguid_with_clat_simple);
         assertEquals(3, stats.size());
 
         assertStatsEntry(stats, "v4-wlan0", 10060, SET_DEFAULT, 0x0, 46860L, 4920L);
@@ -452,7 +440,8 @@
     public void testDoubleClatAccounting() throws Exception {
         mFactory.noteStackedIface("v4-wlan0", "wlan0");
 
-        NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat);
+        final NetworkStats stats =
+                factoryReadNetworkStatsDetail(R.raw.xt_qtaguid_with_clat);
         assertEquals(42, stats.size());
 
         assertStatsEntry(stats, "v4-wlan0", 0, SET_DEFAULT, 0x0, 356L, 276L);
@@ -473,65 +462,78 @@
         assertNoStatsEntry(stats, "wlan0", 1029, SET_DEFAULT, 0x0);
     }
 
-    @Test
-    public void testDoubleClatAccounting100MBDownload() throws Exception {
-        // Downloading 100mb from an ipv4 only destination in a foreground activity
-
-        long appRxBytesBefore = 328684029L;
-        long appRxBytesAfter = 439237478L;
-        assertEquals("App traffic should be ~100MB", 110553449, appRxBytesAfter - appRxBytesBefore);
-
-        long rootRxBytes = 330187296L;
-
-        mFactory.noteStackedIface("v4-wlan0", "wlan0");
-        NetworkStats stats;
-
-        // Stats snapshot before the download
-        stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_100mb_download_before);
-        assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesBefore, 5199872L);
-        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytes, 0L);
-
-        // Stats snapshot after the download
-        stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_100mb_download_after);
-        assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesAfter, 7867488L);
-        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytes, 0L);
-    }
-
-    /**
-     * Copy a {@link Resources#openRawResource(int)} into {@link File} for
-     * testing purposes.
-     */
-    private void stageFile(int rawId, File file) throws Exception {
-        new File(file.getParent()).mkdirs();
-        InputStream in = null;
-        OutputStream out = null;
+    private NetworkStats parseNetworkStatsFromGoldenSample(int resourceId, int initialSize,
+            boolean consumeHeader, boolean checkActive, boolean isUidData) throws IOException {
+        final NetworkStats stats =
+                new NetworkStats(SystemClock.elapsedRealtime(), initialSize);
+        final NetworkStats.Entry entry = new NetworkStats.Entry();
+        ProcFileReader reader = null;
+        int idx = 1;
+        int lastIdx = 1;
         try {
-            in = InstrumentationRegistry.getContext().getResources().openRawResource(rawId);
-            out = new FileOutputStream(file);
-            Streams.copy(in, out);
+            reader = new ProcFileReader(InstrumentationRegistry.getContext().getResources()
+                            .openRawResource(resourceId));
+
+            if (consumeHeader) {
+                reader.finishLine();
+            }
+
+            while (reader.hasMoreData()) {
+                if (isUidData) {
+                    idx = reader.nextInt();
+                    if (idx != lastIdx + 1) {
+                        throw new ProtocolException(
+                                "inconsistent idx=" + idx + " after lastIdx=" + lastIdx);
+                    }
+                    lastIdx = idx;
+                }
+
+                entry.iface = reader.nextString();
+                // Read the uid based information from file. Otherwise, assign with target value.
+                entry.tag = isUidData ? kernelToTag(reader.nextString()) : TAG_NONE;
+                entry.uid = isUidData ? reader.nextInt() : UID_ALL;
+                entry.set = isUidData ? reader.nextInt() : SET_ALL;
+
+                // For fetching active numbers. Dev specific
+                final boolean active = checkActive ? reader.nextInt() != 0 : false;
+
+                // Always include snapshot values
+                entry.rxBytes = reader.nextLong();
+                entry.rxPackets = reader.nextLong();
+                entry.txBytes = reader.nextLong();
+                entry.txPackets = reader.nextLong();
+
+                // Fold in active numbers, but only when active
+                if (active) {
+                    entry.rxBytes += reader.nextLong();
+                    entry.rxPackets += reader.nextLong();
+                    entry.txBytes += reader.nextLong();
+                    entry.txPackets += reader.nextLong();
+                }
+
+                stats.insertEntry(entry);
+                reader.finishLine();
+            }
+        } catch (NullPointerException | NumberFormatException e) {
+            final String errMsg = isUidData
+                    ? "problem parsing idx " + idx : "problem parsing stats";
+            final ProtocolException pe = new ProtocolException(errMsg);
+            pe.initCause(e);
+            throw pe;
         } finally {
-            IoUtils.closeQuietly(in);
-            IoUtils.closeQuietly(out);
+            IoUtils.closeQuietly(reader);
         }
+        return stats;
     }
 
-    private void stageLong(long value, File file) throws Exception {
-        new File(file.getParent()).mkdirs();
-        FileWriter out = null;
-        try {
-            out = new FileWriter(file);
-            out.write(Long.toString(value));
-        } finally {
-            IoUtils.closeQuietly(out);
-        }
-    }
-
-    private File file(String path) throws Exception {
-        return new File(mTestProc, path);
-    }
-
-    private NetworkStats parseDetailedStats(int resourceId) throws Exception {
-        stageFile(resourceId, file("net/xt_qtaguid/stats"));
+    private NetworkStats factoryReadNetworkStatsDetail(int resourceId) throws Exception {
+        // Choose a general detail stats sample size from the experiences to prevent from
+        // frequently allocating buckets.
+        final NetworkStats statsFromResource = parseNetworkStatsFromGoldenSample(resourceId,
+                24 /* initialSize */, true /* consumeHeader */, false /* checkActive */,
+                true /* isUidData */);
+        doReturn(statsFromResource).when(mDeps).getNetworkStatsDetail(anyInt(), any(),
+                anyInt());
         return mFactory.readNetworkStatsDetail();
     }
 
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");
+    }
 }
diff --git a/tests/unit/res/raw/xt_qtaguid_iface_fmt_typical b/tests/unit/res/raw/xt_qtaguid_iface_fmt_typical
deleted file mode 100644
index 656d5bb..0000000
--- a/tests/unit/res/raw/xt_qtaguid_iface_fmt_typical
+++ /dev/null
@@ -1,4 +0,0 @@
-ifname total_skb_rx_bytes total_skb_rx_packets total_skb_tx_bytes total_skb_tx_packets
-rmnet2 4968 35 3081 39
-rmnet1 11153922 8051 190226 2468
-rmnet0 6824 16 5692 10
diff --git a/tests/unit/res/raw/xt_qtaguid_iface_typical b/tests/unit/res/raw/xt_qtaguid_iface_typical
deleted file mode 100644
index 610723a..0000000
--- a/tests/unit/res/raw/xt_qtaguid_iface_typical
+++ /dev/null
@@ -1,6 +0,0 @@
-rmnet3 1 0 0 0 0 20822 501 1149991 815
-rmnet2 1 0 0 0 0 1594 15 1313 15
-rmnet1 1 0 0 0 0 207398 458 166918 565
-rmnet0 1 0 0 0 0 2112 24 700 10
-test1 1 1 2 3 4 5 6 7 8
-test2 0 1 2 3 4 5 6 7 8
diff --git a/tests/unit/res/raw/xt_qtaguid_vpn_incorrect_iface b/tests/unit/res/raw/xt_qtaguid_vpn_incorrect_iface
index fc92715..8b75565 100644
--- a/tests/unit/res/raw/xt_qtaguid_vpn_incorrect_iface
+++ b/tests/unit/res/raw/xt_qtaguid_vpn_incorrect_iface
@@ -1,3 +1,3 @@
 idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
 2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
-3 test1 0x0 1004 0 1100 100 1100 100 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
+3 test1 0x0 1004 0 1100 100 1100 100 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying b/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying
index 1ef1889..2b7cce1 100644
--- a/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying
+++ b/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying
@@ -2,4 +2,4 @@
 2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
 3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0
 4 test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-5 test0 0x0 1004 1 0 0 1650 150 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
+5 test0 0x0 1004 1 0 0 1650 150 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_compression b/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_compression
index 6d6bf55..2028910 100644
--- a/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_compression
+++ b/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_compression
@@ -1,4 +1,4 @@
 idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
 2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
 3 test_nss_tun0 0x0 1002 0 3000 300 3000 300 0 0 0 0 0 0 0 0 0 0 0 0
-4 test0 0x0 1004 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
+4 test0 0x0 1004 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic b/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic
index 2c2e5d2..602f8ec 100644
--- a/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic
+++ b/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic
@@ -3,4 +3,4 @@
 3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0
 4 test_nss_tun0 0x0 1004 0 5000 500 6000 600 0 0 0 0 0 0 0 0 0 0 0 0
 5 test0 0x0 1004 0 8800 800 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-6 test0 0x0 1004 1 0 0 8250 750 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
+6 test0 0x0 1004 1 0 0 8250 750 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn b/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn
index eb0513b..dbe05f0 100644
--- a/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn
+++ b/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn
@@ -6,4 +6,4 @@
 6 test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 7 test0 0x0 1004 1 0 0 1650 150 0 0 0 0 0 0 0 0 0 0 0 0
 8 test1 0x0 1004 0 3850 350 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-9 test1 0x0 1004 1 0 0 1045 95 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
+9 test1 0x0 1004 1 0 0 1045 95 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/tests/unit/res/raw/xt_qtaguid_vpn_rewrite_through_self b/tests/unit/res/raw/xt_qtaguid_vpn_rewrite_through_self
index afcdd71..a84a0fe 100644
--- a/tests/unit/res/raw/xt_qtaguid_vpn_rewrite_through_self
+++ b/tests/unit/res/raw/xt_qtaguid_vpn_rewrite_through_self
@@ -3,4 +3,4 @@
 3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0
 4 test_nss_tun0 0x0 1004 0 0 0 1600 160 0 0 0 0 0 0 0 0 0 0 0 0
 5 test0 0x0 1004 1 0 0 1760 176 0 0 0 0 0 0 0 0 0 0 0 0
-6 test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
+6 test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_duplication b/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_duplication
index d7c7eb9..7a53bc5 100644
--- a/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_duplication
+++ b/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_duplication
@@ -2,4 +2,4 @@
 2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
 3 test_nss_tun0 0x0 1002 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
 4 test0 0x0 1004 0 2200 200 2200 200 0 0 0 0 0 0 0 0 0 0 0 0
-5 test1 0x0 1004 0 2200 200 2200 200 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
+5 test1 0x0 1004 0 2200 200 2200 200 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split b/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split
index 38a3dce..0e4c1b9 100644
--- a/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split
+++ b/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split
@@ -1,4 +1,4 @@
 idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
 2 test_nss_tun0 0x0 1001 0 500 50 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
 3 test0 0x0 1004 0 330 30 660 60 0 0 0 0 0 0 0 0 0 0 0 0
-4 test1 0x0 1004 0 220 20 440 40 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
+4 test1 0x0 1004 0 220 20 440 40 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split_compression b/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split_compression
index d35244b..00a1b65 100644
--- a/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split_compression
+++ b/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split_compression
@@ -1,4 +1,4 @@
 idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
 2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
 3 test0 0x0 1004 0 600 60 600 60 0 0 0 0 0 0 0 0 0 0 0 0
-4 test1 0x0 1004 0 200 20 200 20 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
+4 test1 0x0 1004 0 200 20 200 20 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/tests/unit/res/raw/xt_qtaguid_vpn_with_clat b/tests/unit/res/raw/xt_qtaguid_vpn_with_clat
index 0d893d5..88770a7 100644
--- a/tests/unit/res/raw/xt_qtaguid_vpn_with_clat
+++ b/tests/unit/res/raw/xt_qtaguid_vpn_with_clat
@@ -5,4 +5,4 @@
 5 v4-test0 0x0 1004 1 0 0 1650 150 0 0 0 0 0 0 0 0 0 0 0 0
 6 test0 0x0 0 0 9300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 7 test0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-8 test0 0x0 1029 0 0 0 4650 150 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
+8 test0 0x0 1029 0 0 0 4650 150 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_after b/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_after
deleted file mode 100644
index 12d98ca..0000000
--- a/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_after
+++ /dev/null
@@ -1,189 +0,0 @@
-idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
-2 r_rmnet_data0 0x0 0 0 0 0 392 6 0 0 0 0 0 0 0 0 0 0 392 6
-3 r_rmnet_data0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-4 v4-wlan0 0x0 0 0 58952 2072 2888 65 264 6 0 0 58688 2066 132 3 0 0 2756 62
-5 v4-wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-6 v4-wlan0 0x0 10034 0 6192 11 1445 11 6192 11 0 0 0 0 1445 11 0 0 0 0
-7 v4-wlan0 0x0 10034 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-8 v4-wlan0 0x0 10057 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-9 v4-wlan0 0x0 10057 1 728 7 392 7 0 0 728 7 0 0 0 0 392 7 0 0
-10 v4-wlan0 0x0 10106 0 2232 18 2232 18 0 0 2232 18 0 0 0 0 2232 18 0 0
-11 v4-wlan0 0x0 10106 1 432952718 314238 5442288 121260 432950238 314218 2480 20 0 0 5433900 121029 8388 231 0 0
-12 wlan0 0x0 0 0 330187296 250652 0 0 329106990 236273 226202 1255 854104 13124 0 0 0 0 0 0
-13 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-14 wlan0 0x0 1000 0 77113 272 56151 575 77113 272 0 0 0 0 19191 190 36960 385 0 0
-15 wlan0 0x0 1000 1 20227 80 8356 72 18539 74 1688 6 0 0 7562 66 794 6 0 0
-16 wlan0 0x0 10006 0 80755 92 9122 99 80755 92 0 0 0 0 9122 99 0 0 0 0
-17 wlan0 0x0 10006 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-18 wlan0 0x0 10015 0 4390 7 14824 252 4390 7 0 0 0 0 14824 252 0 0 0 0
-19 wlan0 0x0 10015 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-20 wlan0 0x0 10018 0 4928 11 1741 14 4928 11 0 0 0 0 1741 14 0 0 0 0
-21 wlan0 0x0 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-22 wlan0 0x0 10020 0 21163552 34395 2351650 15326 21162947 34390 605 5 0 0 2351045 15321 605 5 0 0
-23 wlan0 0x0 10020 1 13835740 12938 1548795 6365 13833754 12920 1986 18 0 0 1546809 6347 1986 18 0 0
-24 wlan0 0x0 10023 0 13405 40 5042 44 13405 40 0 0 0 0 5042 44 0 0 0 0
-25 wlan0 0x0 10023 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-26 wlan0 0x0 10034 0 436394741 342648 6237981 80442 436394741 342648 0 0 0 0 6237981 80442 0 0 0 0
-27 wlan0 0x0 10034 1 64860872 51297 1335539 15546 64860872 51297 0 0 0 0 1335539 15546 0 0 0 0
-28 wlan0 0x0 10044 0 17614444 14774 521004 5694 17329882 14432 284562 342 0 0 419974 5408 101030 286 0 0
-29 wlan0 0x0 10044 1 17701 33 3100 28 17701 33 0 0 0 0 3100 28 0 0 0 0
-30 wlan0 0x0 10057 0 12312074 9339 436098 5450 12248060 9263 64014 76 0 0 414224 5388 21874 62 0 0
-31 wlan0 0x0 10057 1 1332953195 954797 31849632 457698 1331933207 953569 1019988 1228 0 0 31702284 456899 147348 799 0 0
-32 wlan0 0x0 10060 0 32972 200 433705 380 32972 200 0 0 0 0 433705 380 0 0 0 0
-33 wlan0 0x0 10060 1 32106 66 37789 87 32106 66 0 0 0 0 37789 87 0 0 0 0
-34 wlan0 0x0 10061 0 7675 23 2509 22 7675 23 0 0 0 0 2509 22 0 0 0 0
-35 wlan0 0x0 10061 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-36 wlan0 0x0 10074 0 38355 82 10447 97 38355 82 0 0 0 0 10447 97 0 0 0 0
-37 wlan0 0x0 10074 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-38 wlan0 0x0 10078 0 49013 79 7167 69 49013 79 0 0 0 0 7167 69 0 0 0 0
-39 wlan0 0x0 10078 1 5872 8 1236 10 5872 8 0 0 0 0 1236 10 0 0 0 0
-40 wlan0 0x0 10082 0 8301 13 1981 15 8301 13 0 0 0 0 1981 15 0 0 0 0
-41 wlan0 0x0 10082 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-42 wlan0 0x0 10086 0 7001 14 1579 15 7001 14 0 0 0 0 1579 15 0 0 0 0
-43 wlan0 0x0 10086 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-44 wlan0 0x0 10090 0 24327795 20224 920502 14661 24327795 20224 0 0 0 0 920502 14661 0 0 0 0
-45 wlan0 0x0 10090 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-46 wlan0 0x0 10092 0 36849 78 12449 81 36849 78 0 0 0 0 12449 81 0 0 0 0
-47 wlan0 0x0 10092 1 60 1 103 1 60 1 0 0 0 0 103 1 0 0 0 0
-48 wlan0 0x0 10095 0 131962 223 37069 241 131962 223 0 0 0 0 37069 241 0 0 0 0
-49 wlan0 0x0 10095 1 12949 21 3930 21 12949 21 0 0 0 0 3930 21 0 0 0 0
-50 wlan0 0x0 10106 0 30899554 22679 632476 12296 30895334 22645 4220 34 0 0 628256 12262 4220 34 0 0
-51 wlan0 0x0 10106 1 88923475 64963 1606962 35612 88917201 64886 3586 29 2688 48 1602032 35535 4930 77 0 0
-52 wlan0 0x40700000000 10020 0 705732 10589 404428 5504 705732 10589 0 0 0 0 404428 5504 0 0 0 0
-53 wlan0 0x40700000000 10020 1 2376 36 1296 18 2376 36 0 0 0 0 1296 18 0 0 0 0
-54 wlan0 0x40800000000 10020 0 34624 146 122525 160 34624 146 0 0 0 0 122525 160 0 0 0 0
-55 wlan0 0x40800000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-56 wlan0 0x40b00000000 10020 0 22411 85 7364 57 22411 85 0 0 0 0 7364 57 0 0 0 0
-57 wlan0 0x40b00000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-58 wlan0 0x120300000000 10020 0 76641 241 32783 169 76641 241 0 0 0 0 32783 169 0 0 0 0
-59 wlan0 0x120300000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-60 wlan0 0x130100000000 10020 0 73101 287 23236 203 73101 287 0 0 0 0 23236 203 0 0 0 0
-61 wlan0 0x130100000000 10020 1 264 4 144 2 264 4 0 0 0 0 144 2 0 0 0 0
-62 wlan0 0x180300000000 10020 0 330648 399 24736 232 330648 399 0 0 0 0 24736 232 0 0 0 0
-63 wlan0 0x180300000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-64 wlan0 0x180400000000 10020 0 21865 59 5022 42 21865 59 0 0 0 0 5022 42 0 0 0 0
-65 wlan0 0x180400000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-66 wlan0 0x300000000000 10020 0 15984 65 26927 57 15984 65 0 0 0 0 26927 57 0 0 0 0
-67 wlan0 0x300000000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-68 wlan0 0x1065fff00000000 10020 0 131871 599 93783 445 131871 599 0 0 0 0 93783 445 0 0 0 0
-69 wlan0 0x1065fff00000000 10020 1 264 4 144 2 264 4 0 0 0 0 144 2 0 0 0 0
-70 wlan0 0x1b24f4600000000 10034 0 15445 42 23329 45 15445 42 0 0 0 0 23329 45 0 0 0 0
-71 wlan0 0x1b24f4600000000 10034 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-72 wlan0 0x1000010000000000 10020 0 5542 9 1364 10 5542 9 0 0 0 0 1364 10 0 0 0 0
-73 wlan0 0x1000010000000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-74 wlan0 0x1000040100000000 10020 0 47196 184 213319 257 47196 184 0 0 0 0 213319 257 0 0 0 0
-75 wlan0 0x1000040100000000 10020 1 60 1 103 1 60 1 0 0 0 0 103 1 0 0 0 0
-76 wlan0 0x1000040700000000 10020 0 11599 50 10786 47 11599 50 0 0 0 0 10786 47 0 0 0 0
-77 wlan0 0x1000040700000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-78 wlan0 0x1000040800000000 10020 0 21902 145 174139 166 21902 145 0 0 0 0 174139 166 0 0 0 0
-79 wlan0 0x1000040800000000 10020 1 8568 88 105743 90 8568 88 0 0 0 0 105743 90 0 0 0 0
-80 wlan0 0x1000100300000000 10020 0 55213 118 194551 199 55213 118 0 0 0 0 194551 199 0 0 0 0
-81 wlan0 0x1000100300000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-82 wlan0 0x1000120300000000 10020 0 50826 74 21153 70 50826 74 0 0 0 0 21153 70 0 0 0 0
-83 wlan0 0x1000120300000000 10020 1 72 1 175 2 72 1 0 0 0 0 175 2 0 0 0 0
-84 wlan0 0x1000180300000000 10020 0 744198 657 65437 592 744198 657 0 0 0 0 65437 592 0 0 0 0
-85 wlan0 0x1000180300000000 10020 1 144719 132 10989 108 144719 132 0 0 0 0 10989 108 0 0 0 0
-86 wlan0 0x1000180600000000 10020 0 4599 8 1928 10 4599 8 0 0 0 0 1928 10 0 0 0 0
-87 wlan0 0x1000180600000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-88 wlan0 0x1000250000000000 10020 0 57740 98 13076 88 57740 98 0 0 0 0 13076 88 0 0 0 0
-89 wlan0 0x1000250000000000 10020 1 328 3 414 4 207 2 121 1 0 0 293 3 121 1 0 0
-90 wlan0 0x1000300000000000 10020 0 7675 30 31331 32 7675 30 0 0 0 0 31331 32 0 0 0 0
-91 wlan0 0x1000300000000000 10020 1 30173 97 101335 100 30173 97 0 0 0 0 101335 100 0 0 0 0
-92 wlan0 0x1000310200000000 10020 0 1681 9 2194 9 1681 9 0 0 0 0 2194 9 0 0 0 0
-93 wlan0 0x1000310200000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-94 wlan0 0x1000360000000000 10020 0 5606 20 2831 20 5606 20 0 0 0 0 2831 20 0 0 0 0
-95 wlan0 0x1000360000000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-96 wlan0 0x11065fff00000000 10020 0 18363 91 83367 104 18363 91 0 0 0 0 83367 104 0 0 0 0
-97 wlan0 0x11065fff00000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-98 wlan0 0x3000009600000000 10020 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-99 wlan0 0x3000009600000000 10020 1 6163 18 2424 18 6163 18 0 0 0 0 2424 18 0 0 0 0
-100 wlan0 0x3000009800000000 10020 0 23337 46 8723 39 23337 46 0 0 0 0 8723 39 0 0 0 0
-101 wlan0 0x3000009800000000 10020 1 33744 93 72437 89 33744 93 0 0 0 0 72437 89 0 0 0 0
-102 wlan0 0x3000020000000000 10020 0 4124 11 8969 19 4124 11 0 0 0 0 8969 19 0 0 0 0
-103 wlan0 0x3000020000000000 10020 1 5993 11 3815 14 5993 11 0 0 0 0 3815 14 0 0 0 0
-104 wlan0 0x3000040100000000 10020 0 113809 342 135666 308 113809 342 0 0 0 0 135666 308 0 0 0 0
-105 wlan0 0x3000040100000000 10020 1 142508 642 500579 637 142508 642 0 0 0 0 500579 637 0 0 0 0
-106 wlan0 0x3000040700000000 10020 0 365815 5119 213340 2733 365815 5119 0 0 0 0 213340 2733 0 0 0 0
-107 wlan0 0x3000040700000000 10020 1 30747 130 18408 100 30747 130 0 0 0 0 18408 100 0 0 0 0
-108 wlan0 0x3000040800000000 10020 0 34672 112 68623 92 34672 112 0 0 0 0 68623 92 0 0 0 0
-109 wlan0 0x3000040800000000 10020 1 78443 199 140944 192 78443 199 0 0 0 0 140944 192 0 0 0 0
-110 wlan0 0x3000040b00000000 10020 0 14949 33 4017 26 14949 33 0 0 0 0 4017 26 0 0 0 0
-111 wlan0 0x3000040b00000000 10020 1 996 15 576 8 996 15 0 0 0 0 576 8 0 0 0 0
-112 wlan0 0x3000090000000000 10020 0 11826 67 7309 52 11826 67 0 0 0 0 7309 52 0 0 0 0
-113 wlan0 0x3000090000000000 10020 1 24805 41 4785 41 24805 41 0 0 0 0 4785 41 0 0 0 0
-114 wlan0 0x3000100300000000 10020 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-115 wlan0 0x3000100300000000 10020 1 3112 10 1628 10 3112 10 0 0 0 0 1628 10 0 0 0 0
-116 wlan0 0x3000120300000000 10020 0 38249 107 20374 85 38249 107 0 0 0 0 20374 85 0 0 0 0
-117 wlan0 0x3000120300000000 10020 1 122581 174 36792 143 122581 174 0 0 0 0 36792 143 0 0 0 0
-118 wlan0 0x3000130100000000 10020 0 2700 41 1524 21 2700 41 0 0 0 0 1524 21 0 0 0 0
-119 wlan0 0x3000130100000000 10020 1 22515 59 8366 52 22515 59 0 0 0 0 8366 52 0 0 0 0
-120 wlan0 0x3000180200000000 10020 0 6411 18 14511 20 6411 18 0 0 0 0 14511 20 0 0 0 0
-121 wlan0 0x3000180200000000 10020 1 336 5 319 4 336 5 0 0 0 0 319 4 0 0 0 0
-122 wlan0 0x3000180300000000 10020 0 129301 136 17622 97 129301 136 0 0 0 0 17622 97 0 0 0 0
-123 wlan0 0x3000180300000000 10020 1 464787 429 41703 336 464787 429 0 0 0 0 41703 336 0 0 0 0
-124 wlan0 0x3000180400000000 10020 0 11014 39 2787 25 11014 39 0 0 0 0 2787 25 0 0 0 0
-125 wlan0 0x3000180400000000 10020 1 144040 139 7540 80 144040 139 0 0 0 0 7540 80 0 0 0 0
-126 wlan0 0x3000210100000000 10020 0 10278 44 4579 33 10278 44 0 0 0 0 4579 33 0 0 0 0
-127 wlan0 0x3000210100000000 10020 1 31151 73 14159 47 31151 73 0 0 0 0 14159 47 0 0 0 0
-128 wlan0 0x3000250000000000 10020 0 132 2 72 1 132 2 0 0 0 0 72 1 0 0 0 0
-129 wlan0 0x3000250000000000 10020 1 76614 143 17711 130 76080 137 534 6 0 0 17177 124 534 6 0 0
-130 wlan0 0x3000260100000000 10020 0 9426 26 3535 20 9426 26 0 0 0 0 3535 20 0 0 0 0
-131 wlan0 0x3000260100000000 10020 1 468 7 288 4 468 7 0 0 0 0 288 4 0 0 0 0
-132 wlan0 0x3000300000000000 10020 0 7241 29 12055 26 7241 29 0 0 0 0 12055 26 0 0 0 0
-133 wlan0 0x3000300000000000 10020 1 3273 23 11232 21 3273 23 0 0 0 0 11232 21 0 0 0 0
-134 wlan0 0x3000310000000000 10020 0 132 2 72 1 132 2 0 0 0 0 72 1 0 0 0 0
-135 wlan0 0x3000310000000000 10020 1 53425 64 8721 62 53425 64 0 0 0 0 8721 62 0 0 0 0
-136 wlan0 0x3000310500000000 10020 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-137 wlan0 0x3000310500000000 10020 1 9929 16 3879 18 9929 16 0 0 0 0 3879 18 0 0 0 0
-138 wlan0 0x3000320100000000 10020 0 6844 14 3745 13 6844 14 0 0 0 0 3745 13 0 0 0 0
-139 wlan0 0x3000320100000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-140 wlan0 0x3000360000000000 10020 0 8855 43 4749 31 8855 43 0 0 0 0 4749 31 0 0 0 0
-141 wlan0 0x3000360000000000 10020 1 5597 19 2456 19 5597 19 0 0 0 0 2456 19 0 0 0 0
-142 wlan0 0x3010000000000000 10090 0 605140 527 38435 429 605140 527 0 0 0 0 38435 429 0 0 0 0
-143 wlan0 0x3010000000000000 10090 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-144 wlan0 0x31065fff00000000 10020 0 22011 67 29665 64 22011 67 0 0 0 0 29665 64 0 0 0 0
-145 wlan0 0x31065fff00000000 10020 1 10695 34 18347 35 10695 34 0 0 0 0 18347 35 0 0 0 0
-146 wlan0 0x32e544f900000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-147 wlan0 0x32e544f900000000 10034 1 40143 54 7299 61 40143 54 0 0 0 0 7299 61 0 0 0 0
-148 wlan0 0x58872a4400000000 10018 0 4928 11 1669 13 4928 11 0 0 0 0 1669 13 0 0 0 0
-149 wlan0 0x58872a4400000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-150 wlan0 0x5caeaa7b00000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-151 wlan0 0x5caeaa7b00000000 10034 1 74971 73 7103 75 74971 73 0 0 0 0 7103 75 0 0 0 0
-152 wlan0 0x9e00923800000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-153 wlan0 0x9e00923800000000 10034 1 72385 98 13072 110 72385 98 0 0 0 0 13072 110 0 0 0 0
-154 wlan0 0xb972bdd400000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-155 wlan0 0xb972bdd400000000 10034 1 15282 24 3034 27 15282 24 0 0 0 0 3034 27 0 0 0 0
-156 wlan0 0xc7c9f7ba00000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-157 wlan0 0xc7c9f7ba00000000 10034 1 194915 185 13316 138 194915 185 0 0 0 0 13316 138 0 0 0 0
-158 wlan0 0xc9395b2600000000 10034 0 6991 13 6215 14 6991 13 0 0 0 0 6215 14 0 0 0 0
-159 wlan0 0xc9395b2600000000 10034 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-160 wlan0 0xdaddf21100000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-161 wlan0 0xdaddf21100000000 10034 1 928676 849 81570 799 928676 849 0 0 0 0 81570 799 0 0 0 0
-162 wlan0 0xe8d195d100000000 10020 0 516 8 288 4 516 8 0 0 0 0 288 4 0 0 0 0
-163 wlan0 0xe8d195d100000000 10020 1 5905 15 2622 15 5905 15 0 0 0 0 2622 15 0 0 0 0
-164 wlan0 0xe8d195d100000000 10034 0 236640 524 312523 555 236640 524 0 0 0 0 312523 555 0 0 0 0
-165 wlan0 0xe8d195d100000000 10034 1 319028 539 188776 553 319028 539 0 0 0 0 188776 553 0 0 0 0
-166 wlan0 0xffffff0100000000 10006 0 80755 92 9122 99 80755 92 0 0 0 0 9122 99 0 0 0 0
-167 wlan0 0xffffff0100000000 10006 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-168 wlan0 0xffffff0100000000 10020 0 17874405 14068 223987 3065 17874405 14068 0 0 0 0 223987 3065 0 0 0 0
-169 wlan0 0xffffff0100000000 10020 1 11011258 8672 177693 2407 11011258 8672 0 0 0 0 177693 2407 0 0 0 0
-170 wlan0 0xffffff0100000000 10034 0 436062595 341880 5843990 79630 436062595 341880 0 0 0 0 5843990 79630 0 0 0 0
-171 wlan0 0xffffff0100000000 10034 1 63201220 49447 1005882 13713 63201220 49447 0 0 0 0 1005882 13713 0 0 0 0
-172 wlan0 0xffffff0100000000 10044 0 17159287 13702 356212 4778 17159287 13702 0 0 0 0 356212 4778 0 0 0 0
-173 wlan0 0xffffff0100000000 10044 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-174 wlan0 0xffffff0100000000 10078 0 10439 17 1665 15 10439 17 0 0 0 0 1665 15 0 0 0 0
-175 wlan0 0xffffff0100000000 10078 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-176 wlan0 0xffffff0100000000 10090 0 23722655 19697 881995 14231 23722655 19697 0 0 0 0 881995 14231 0 0 0 0
-177 wlan0 0xffffff0100000000 10090 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-178 wlan0 0xffffff0500000000 1000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-179 wlan0 0xffffff0500000000 1000 1 1592 5 314 1 0 0 1592 5 0 0 0 0 314 1 0 0
-180 wlan0 0xffffff0600000000 1000 0 0 0 36960 385 0 0 0 0 0 0 0 0 36960 385 0 0
-181 wlan0 0xffffff0600000000 1000 1 96 1 480 5 0 0 96 1 0 0 0 0 480 5 0 0
-182 wlan0 0xffffff0700000000 1000 0 38732 229 16567 163 38732 229 0 0 0 0 16567 163 0 0 0 0
-183 wlan0 0xffffff0700000000 1000 1 18539 74 7562 66 18539 74 0 0 0 0 7562 66 0 0 0 0
-184 wlan0 0xffffff0900000000 1000 0 38381 43 2624 27 38381 43 0 0 0 0 2624 27 0 0 0 0
-185 wlan0 0xffffff0900000000 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-186 dummy0 0x0 0 0 0 0 168 3 0 0 0 0 0 0 0 0 0 0 168 3
-187 dummy0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-188 wlan0 0x0 1029 0 0 0 8524052 130894 0 0 0 0 0 0 7871216 121284 108568 1325 544268 8285
-189 wlan0 0x0 1029 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_before b/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_before
deleted file mode 100644
index ce4bcc3..0000000
--- a/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_before
+++ /dev/null
@@ -1,187 +0,0 @@
-idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
-2 r_rmnet_data0 0x0 0 0 0 0 392 6 0 0 0 0 0 0 0 0 0 0 392 6
-3 r_rmnet_data0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-4 v4-wlan0 0x0 0 0 58848 2070 2836 64 160 4 0 0 58688 2066 80 2 0 0 2756 62
-5 v4-wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-6 v4-wlan0 0x0 10034 0 6192 11 1445 11 6192 11 0 0 0 0 1445 11 0 0 0 0
-7 v4-wlan0 0x0 10034 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-8 v4-wlan0 0x0 10057 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-9 v4-wlan0 0x0 10057 1 728 7 392 7 0 0 728 7 0 0 0 0 392 7 0 0
-10 v4-wlan0 0x0 10106 0 1488 12 1488 12 0 0 1488 12 0 0 0 0 1488 12 0 0
-11 v4-wlan0 0x0 10106 1 323981189 235142 3509032 84542 323979453 235128 1736 14 0 0 3502676 84363 6356 179 0 0
-12 wlan0 0x0 0 0 330187296 250652 0 0 329106990 236273 226202 1255 854104 13124 0 0 0 0 0 0
-13 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-14 wlan0 0x0 1000 0 77113 272 56151 575 77113 272 0 0 0 0 19191 190 36960 385 0 0
-15 wlan0 0x0 1000 1 20227 80 8356 72 18539 74 1688 6 0 0 7562 66 794 6 0 0
-16 wlan0 0x0 10006 0 80755 92 9122 99 80755 92 0 0 0 0 9122 99 0 0 0 0
-17 wlan0 0x0 10006 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-18 wlan0 0x0 10015 0 4390 7 14824 252 4390 7 0 0 0 0 14824 252 0 0 0 0
-19 wlan0 0x0 10015 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-20 wlan0 0x0 10018 0 4928 11 1741 14 4928 11 0 0 0 0 1741 14 0 0 0 0
-21 wlan0 0x0 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-22 wlan0 0x0 10020 0 21141412 34316 2329881 15262 21140807 34311 605 5 0 0 2329276 15257 605 5 0 0
-23 wlan0 0x0 10020 1 13835740 12938 1548555 6362 13833754 12920 1986 18 0 0 1546569 6344 1986 18 0 0
-24 wlan0 0x0 10023 0 13405 40 5042 44 13405 40 0 0 0 0 5042 44 0 0 0 0
-25 wlan0 0x0 10023 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-26 wlan0 0x0 10034 0 436394741 342648 6237981 80442 436394741 342648 0 0 0 0 6237981 80442 0 0 0 0
-27 wlan0 0x0 10034 1 64860872 51297 1335539 15546 64860872 51297 0 0 0 0 1335539 15546 0 0 0 0
-28 wlan0 0x0 10044 0 17614444 14774 521004 5694 17329882 14432 284562 342 0 0 419974 5408 101030 286 0 0
-29 wlan0 0x0 10044 1 17701 33 3100 28 17701 33 0 0 0 0 3100 28 0 0 0 0
-30 wlan0 0x0 10057 0 12311735 9335 435954 5448 12247721 9259 64014 76 0 0 414080 5386 21874 62 0 0
-31 wlan0 0x0 10057 1 1332953195 954797 31849632 457698 1331933207 953569 1019988 1228 0 0 31702284 456899 147348 799 0 0
-32 wlan0 0x0 10060 0 32972 200 433705 380 32972 200 0 0 0 0 433705 380 0 0 0 0
-33 wlan0 0x0 10060 1 32106 66 37789 87 32106 66 0 0 0 0 37789 87 0 0 0 0
-34 wlan0 0x0 10061 0 7675 23 2509 22 7675 23 0 0 0 0 2509 22 0 0 0 0
-35 wlan0 0x0 10061 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-36 wlan0 0x0 10074 0 38355 82 10447 97 38355 82 0 0 0 0 10447 97 0 0 0 0
-37 wlan0 0x0 10074 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-38 wlan0 0x0 10078 0 49013 79 7167 69 49013 79 0 0 0 0 7167 69 0 0 0 0
-39 wlan0 0x0 10078 1 5872 8 1236 10 5872 8 0 0 0 0 1236 10 0 0 0 0
-40 wlan0 0x0 10082 0 8301 13 1981 15 8301 13 0 0 0 0 1981 15 0 0 0 0
-41 wlan0 0x0 10082 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-42 wlan0 0x0 10086 0 7001 14 1579 15 7001 14 0 0 0 0 1579 15 0 0 0 0
-43 wlan0 0x0 10086 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-44 wlan0 0x0 10090 0 24327795 20224 920502 14661 24327795 20224 0 0 0 0 920502 14661 0 0 0 0
-45 wlan0 0x0 10090 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-46 wlan0 0x0 10092 0 36849 78 12449 81 36849 78 0 0 0 0 12449 81 0 0 0 0
-47 wlan0 0x0 10092 1 60 1 103 1 60 1 0 0 0 0 103 1 0 0 0 0
-48 wlan0 0x0 10095 0 131962 223 37069 241 131962 223 0 0 0 0 37069 241 0 0 0 0
-49 wlan0 0x0 10095 1 12949 21 3930 21 12949 21 0 0 0 0 3930 21 0 0 0 0
-50 wlan0 0x0 10106 0 30899554 22679 632476 12296 30895334 22645 4220 34 0 0 628256 12262 4220 34 0 0
-51 wlan0 0x0 10106 1 88922349 64952 1605126 35599 88916075 64875 3586 29 2688 48 1600196 35522 4930 77 0 0
-52 wlan0 0x40700000000 10020 0 705732 10589 404428 5504 705732 10589 0 0 0 0 404428 5504 0 0 0 0
-53 wlan0 0x40700000000 10020 1 2376 36 1296 18 2376 36 0 0 0 0 1296 18 0 0 0 0
-54 wlan0 0x40800000000 10020 0 34624 146 122525 160 34624 146 0 0 0 0 122525 160 0 0 0 0
-55 wlan0 0x40800000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-56 wlan0 0x40b00000000 10020 0 22411 85 7364 57 22411 85 0 0 0 0 7364 57 0 0 0 0
-57 wlan0 0x40b00000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-58 wlan0 0x120300000000 10020 0 76641 241 32783 169 76641 241 0 0 0 0 32783 169 0 0 0 0
-59 wlan0 0x120300000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-60 wlan0 0x130100000000 10020 0 73101 287 23236 203 73101 287 0 0 0 0 23236 203 0 0 0 0
-61 wlan0 0x130100000000 10020 1 264 4 144 2 264 4 0 0 0 0 144 2 0 0 0 0
-62 wlan0 0x180300000000 10020 0 330648 399 24736 232 330648 399 0 0 0 0 24736 232 0 0 0 0
-63 wlan0 0x180300000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-64 wlan0 0x180400000000 10020 0 21865 59 5022 42 21865 59 0 0 0 0 5022 42 0 0 0 0
-65 wlan0 0x180400000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-66 wlan0 0x300000000000 10020 0 15984 65 26927 57 15984 65 0 0 0 0 26927 57 0 0 0 0
-67 wlan0 0x300000000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-68 wlan0 0x1065fff00000000 10020 0 131871 599 93783 445 131871 599 0 0 0 0 93783 445 0 0 0 0
-69 wlan0 0x1065fff00000000 10020 1 264 4 144 2 264 4 0 0 0 0 144 2 0 0 0 0
-70 wlan0 0x1b24f4600000000 10034 0 15445 42 23329 45 15445 42 0 0 0 0 23329 45 0 0 0 0
-71 wlan0 0x1b24f4600000000 10034 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-72 wlan0 0x1000010000000000 10020 0 5542 9 1364 10 5542 9 0 0 0 0 1364 10 0 0 0 0
-73 wlan0 0x1000010000000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-74 wlan0 0x1000040100000000 10020 0 47196 184 213319 257 47196 184 0 0 0 0 213319 257 0 0 0 0
-75 wlan0 0x1000040100000000 10020 1 60 1 103 1 60 1 0 0 0 0 103 1 0 0 0 0
-76 wlan0 0x1000040700000000 10020 0 11599 50 10786 47 11599 50 0 0 0 0 10786 47 0 0 0 0
-77 wlan0 0x1000040700000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-78 wlan0 0x1000040800000000 10020 0 21902 145 174139 166 21902 145 0 0 0 0 174139 166 0 0 0 0
-79 wlan0 0x1000040800000000 10020 1 8568 88 105743 90 8568 88 0 0 0 0 105743 90 0 0 0 0
-80 wlan0 0x1000100300000000 10020 0 55213 118 194551 199 55213 118 0 0 0 0 194551 199 0 0 0 0
-81 wlan0 0x1000100300000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-82 wlan0 0x1000120300000000 10020 0 50826 74 21153 70 50826 74 0 0 0 0 21153 70 0 0 0 0
-83 wlan0 0x1000120300000000 10020 1 72 1 175 2 72 1 0 0 0 0 175 2 0 0 0 0
-84 wlan0 0x1000180300000000 10020 0 744198 657 65437 592 744198 657 0 0 0 0 65437 592 0 0 0 0
-85 wlan0 0x1000180300000000 10020 1 144719 132 10989 108 144719 132 0 0 0 0 10989 108 0 0 0 0
-86 wlan0 0x1000180600000000 10020 0 4599 8 1928 10 4599 8 0 0 0 0 1928 10 0 0 0 0
-87 wlan0 0x1000180600000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-88 wlan0 0x1000250000000000 10020 0 57740 98 13076 88 57740 98 0 0 0 0 13076 88 0 0 0 0
-89 wlan0 0x1000250000000000 10020 1 328 3 414 4 207 2 121 1 0 0 293 3 121 1 0 0
-90 wlan0 0x1000300000000000 10020 0 7675 30 31331 32 7675 30 0 0 0 0 31331 32 0 0 0 0
-91 wlan0 0x1000300000000000 10020 1 30173 97 101335 100 30173 97 0 0 0 0 101335 100 0 0 0 0
-92 wlan0 0x1000310200000000 10020 0 1681 9 2194 9 1681 9 0 0 0 0 2194 9 0 0 0 0
-93 wlan0 0x1000310200000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-94 wlan0 0x1000360000000000 10020 0 5606 20 2831 20 5606 20 0 0 0 0 2831 20 0 0 0 0
-95 wlan0 0x1000360000000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-96 wlan0 0x11065fff00000000 10020 0 18363 91 83367 104 18363 91 0 0 0 0 83367 104 0 0 0 0
-97 wlan0 0x11065fff00000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-98 wlan0 0x3000009600000000 10020 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-99 wlan0 0x3000009600000000 10020 1 6163 18 2424 18 6163 18 0 0 0 0 2424 18 0 0 0 0
-100 wlan0 0x3000009800000000 10020 0 23337 46 8723 39 23337 46 0 0 0 0 8723 39 0 0 0 0
-101 wlan0 0x3000009800000000 10020 1 33744 93 72437 89 33744 93 0 0 0 0 72437 89 0 0 0 0
-102 wlan0 0x3000020000000000 10020 0 4124 11 8969 19 4124 11 0 0 0 0 8969 19 0 0 0 0
-103 wlan0 0x3000020000000000 10020 1 5993 11 3815 14 5993 11 0 0 0 0 3815 14 0 0 0 0
-104 wlan0 0x3000040100000000 10020 0 106718 322 121557 287 106718 322 0 0 0 0 121557 287 0 0 0 0
-105 wlan0 0x3000040100000000 10020 1 142508 642 500579 637 142508 642 0 0 0 0 500579 637 0 0 0 0
-106 wlan0 0x3000040700000000 10020 0 365419 5113 213124 2730 365419 5113 0 0 0 0 213124 2730 0 0 0 0
-107 wlan0 0x3000040700000000 10020 1 30747 130 18408 100 30747 130 0 0 0 0 18408 100 0 0 0 0
-108 wlan0 0x3000040800000000 10020 0 34672 112 68623 92 34672 112 0 0 0 0 68623 92 0 0 0 0
-109 wlan0 0x3000040800000000 10020 1 78443 199 140944 192 78443 199 0 0 0 0 140944 192 0 0 0 0
-110 wlan0 0x3000040b00000000 10020 0 14949 33 4017 26 14949 33 0 0 0 0 4017 26 0 0 0 0
-111 wlan0 0x3000040b00000000 10020 1 996 15 576 8 996 15 0 0 0 0 576 8 0 0 0 0
-112 wlan0 0x3000090000000000 10020 0 4017 28 3610 25 4017 28 0 0 0 0 3610 25 0 0 0 0
-113 wlan0 0x3000090000000000 10020 1 24805 41 4545 38 24805 41 0 0 0 0 4545 38 0 0 0 0
-114 wlan0 0x3000100300000000 10020 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-115 wlan0 0x3000100300000000 10020 1 3112 10 1628 10 3112 10 0 0 0 0 1628 10 0 0 0 0
-116 wlan0 0x3000120300000000 10020 0 38249 107 20374 85 38249 107 0 0 0 0 20374 85 0 0 0 0
-117 wlan0 0x3000120300000000 10020 1 122581 174 36792 143 122581 174 0 0 0 0 36792 143 0 0 0 0
-118 wlan0 0x3000130100000000 10020 0 2700 41 1524 21 2700 41 0 0 0 0 1524 21 0 0 0 0
-119 wlan0 0x3000130100000000 10020 1 22515 59 8366 52 22515 59 0 0 0 0 8366 52 0 0 0 0
-120 wlan0 0x3000180200000000 10020 0 6411 18 14511 20 6411 18 0 0 0 0 14511 20 0 0 0 0
-121 wlan0 0x3000180200000000 10020 1 336 5 319 4 336 5 0 0 0 0 319 4 0 0 0 0
-122 wlan0 0x3000180300000000 10020 0 129301 136 17622 97 129301 136 0 0 0 0 17622 97 0 0 0 0
-123 wlan0 0x3000180300000000 10020 1 464787 429 41703 336 464787 429 0 0 0 0 41703 336 0 0 0 0
-124 wlan0 0x3000180400000000 10020 0 11014 39 2787 25 11014 39 0 0 0 0 2787 25 0 0 0 0
-125 wlan0 0x3000180400000000 10020 1 144040 139 7540 80 144040 139 0 0 0 0 7540 80 0 0 0 0
-126 wlan0 0x3000210100000000 10020 0 10278 44 4579 33 10278 44 0 0 0 0 4579 33 0 0 0 0
-127 wlan0 0x3000210100000000 10020 1 31151 73 14159 47 31151 73 0 0 0 0 14159 47 0 0 0 0
-128 wlan0 0x3000250000000000 10020 0 132 2 72 1 132 2 0 0 0 0 72 1 0 0 0 0
-129 wlan0 0x3000250000000000 10020 1 76614 143 17711 130 76080 137 534 6 0 0 17177 124 534 6 0 0
-130 wlan0 0x3000260100000000 10020 0 9426 26 3535 20 9426 26 0 0 0 0 3535 20 0 0 0 0
-131 wlan0 0x3000260100000000 10020 1 468 7 288 4 468 7 0 0 0 0 288 4 0 0 0 0
-132 wlan0 0x3000300000000000 10020 0 7241 29 12055 26 7241 29 0 0 0 0 12055 26 0 0 0 0
-133 wlan0 0x3000300000000000 10020 1 3273 23 11232 21 3273 23 0 0 0 0 11232 21 0 0 0 0
-134 wlan0 0x3000310000000000 10020 0 132 2 72 1 132 2 0 0 0 0 72 1 0 0 0 0
-135 wlan0 0x3000310000000000 10020 1 53425 64 8721 62 53425 64 0 0 0 0 8721 62 0 0 0 0
-136 wlan0 0x3000310500000000 10020 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-137 wlan0 0x3000310500000000 10020 1 9929 16 3879 18 9929 16 0 0 0 0 3879 18 0 0 0 0
-138 wlan0 0x3000360000000000 10020 0 8855 43 4749 31 8855 43 0 0 0 0 4749 31 0 0 0 0
-139 wlan0 0x3000360000000000 10020 1 5597 19 2456 19 5597 19 0 0 0 0 2456 19 0 0 0 0
-140 wlan0 0x3010000000000000 10090 0 605140 527 38435 429 605140 527 0 0 0 0 38435 429 0 0 0 0
-141 wlan0 0x3010000000000000 10090 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-142 wlan0 0x31065fff00000000 10020 0 22011 67 29665 64 22011 67 0 0 0 0 29665 64 0 0 0 0
-143 wlan0 0x31065fff00000000 10020 1 10695 34 18347 35 10695 34 0 0 0 0 18347 35 0 0 0 0
-144 wlan0 0x32e544f900000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-145 wlan0 0x32e544f900000000 10034 1 40143 54 7299 61 40143 54 0 0 0 0 7299 61 0 0 0 0
-146 wlan0 0x58872a4400000000 10018 0 4928 11 1669 13 4928 11 0 0 0 0 1669 13 0 0 0 0
-147 wlan0 0x58872a4400000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-148 wlan0 0x5caeaa7b00000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-149 wlan0 0x5caeaa7b00000000 10034 1 74971 73 7103 75 74971 73 0 0 0 0 7103 75 0 0 0 0
-150 wlan0 0x9e00923800000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-151 wlan0 0x9e00923800000000 10034 1 72385 98 13072 110 72385 98 0 0 0 0 13072 110 0 0 0 0
-152 wlan0 0xb972bdd400000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-153 wlan0 0xb972bdd400000000 10034 1 15282 24 3034 27 15282 24 0 0 0 0 3034 27 0 0 0 0
-154 wlan0 0xc7c9f7ba00000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-155 wlan0 0xc7c9f7ba00000000 10034 1 194915 185 13316 138 194915 185 0 0 0 0 13316 138 0 0 0 0
-156 wlan0 0xc9395b2600000000 10034 0 6991 13 6215 14 6991 13 0 0 0 0 6215 14 0 0 0 0
-157 wlan0 0xc9395b2600000000 10034 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-158 wlan0 0xdaddf21100000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-159 wlan0 0xdaddf21100000000 10034 1 928676 849 81570 799 928676 849 0 0 0 0 81570 799 0 0 0 0
-160 wlan0 0xe8d195d100000000 10020 0 516 8 288 4 516 8 0 0 0 0 288 4 0 0 0 0
-161 wlan0 0xe8d195d100000000 10020 1 5905 15 2622 15 5905 15 0 0 0 0 2622 15 0 0 0 0
-162 wlan0 0xe8d195d100000000 10034 0 236640 524 312523 555 236640 524 0 0 0 0 312523 555 0 0 0 0
-163 wlan0 0xe8d195d100000000 10034 1 319028 539 188776 553 319028 539 0 0 0 0 188776 553 0 0 0 0
-164 wlan0 0xffffff0100000000 10006 0 80755 92 9122 99 80755 92 0 0 0 0 9122 99 0 0 0 0
-165 wlan0 0xffffff0100000000 10006 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-166 wlan0 0xffffff0100000000 10020 0 17874405 14068 223987 3065 17874405 14068 0 0 0 0 223987 3065 0 0 0 0
-167 wlan0 0xffffff0100000000 10020 1 11011258 8672 177693 2407 11011258 8672 0 0 0 0 177693 2407 0 0 0 0
-168 wlan0 0xffffff0100000000 10034 0 436062595 341880 5843990 79630 436062595 341880 0 0 0 0 5843990 79630 0 0 0 0
-169 wlan0 0xffffff0100000000 10034 1 63201220 49447 1005882 13713 63201220 49447 0 0 0 0 1005882 13713 0 0 0 0
-170 wlan0 0xffffff0100000000 10044 0 17159287 13702 356212 4778 17159287 13702 0 0 0 0 356212 4778 0 0 0 0
-171 wlan0 0xffffff0100000000 10044 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-172 wlan0 0xffffff0100000000 10078 0 10439 17 1665 15 10439 17 0 0 0 0 1665 15 0 0 0 0
-173 wlan0 0xffffff0100000000 10078 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-174 wlan0 0xffffff0100000000 10090 0 23722655 19697 881995 14231 23722655 19697 0 0 0 0 881995 14231 0 0 0 0
-175 wlan0 0xffffff0100000000 10090 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-176 wlan0 0xffffff0500000000 1000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-177 wlan0 0xffffff0500000000 1000 1 1592 5 314 1 0 0 1592 5 0 0 0 0 314 1 0 0
-178 wlan0 0xffffff0600000000 1000 0 0 0 36960 385 0 0 0 0 0 0 0 0 36960 385 0 0
-179 wlan0 0xffffff0600000000 1000 1 96 1 480 5 0 0 96 1 0 0 0 0 480 5 0 0
-180 wlan0 0xffffff0700000000 1000 0 38732 229 16567 163 38732 229 0 0 0 0 16567 163 0 0 0 0
-181 wlan0 0xffffff0700000000 1000 1 18539 74 7562 66 18539 74 0 0 0 0 7562 66 0 0 0 0
-182 wlan0 0xffffff0900000000 1000 0 38381 43 2624 27 38381 43 0 0 0 0 2624 27 0 0 0 0
-183 wlan0 0xffffff0900000000 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-184 dummy0 0x0 0 0 0 0 168 3 0 0 0 0 0 0 0 0 0 0 168 3
-185 dummy0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-186 wlan0 0x0 1029 0 0 0 5855801 94173 0 0 0 0 0 0 5208040 84634 103637 1256 544124 8283
-187 wlan0 0x0 1029 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0