Merge "Remove logic from NetworkMonitorManager wrapper"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index c4c79c6..bb6d655 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -41,6 +41,9 @@
       "name": "connectivity_native_test"
     },
     {
+      "name": "libclat_test"
+    },
+    {
       "name": "netd_updatable_unit_test"
     },
     {
@@ -68,16 +71,10 @@
       "keywords": ["netd-device-kernel-4.9", "netd-device-kernel-4.14"]
     },
     {
-      "name": "libclat_test"
-    },
-    {
       "name": "traffic_controller_unit_test",
       "keywords": ["netd-device-kernel-4.9", "netd-device-kernel-4.14"]
     },
     {
-      "name": "libnetworkstats_test"
-    },
-    {
       "name": "FrameworksNetDeflakeTest"
     }
   ],
diff --git a/Tethering/apex/manifest.json b/Tethering/apex/manifest.json
index 88f13b2..3cb03ed 100644
--- a/Tethering/apex/manifest.json
+++ b/Tethering/apex/manifest.json
@@ -1,4 +1,4 @@
 {
   "name": "com.android.tethering",
-  "version": 319999900
+  "version": 339990000
 }
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 3699f7a..5869f2b 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -36,9 +36,11 @@
 import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV4;
 import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6;
 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
+import static com.android.testutils.DeviceInfoUtils.KVersion;
 import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
 
 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;
@@ -1067,23 +1069,43 @@
                 false /* usingBpf */);
     }
 
-    private static boolean isUdpOffloadSupportedByKernel() {
-        final String kVersionString = VintfRuntimeInfo.getKernelVersion();
-        // Kernel version which is older than 4.14 doesn't support UDP offload absolutely. Kernel
-        // version which is between 4.14 and 5.8 support UDP offload probably. Simply apply kernel
-        // 4.14 to be threshold first and monitor on what devices tests fail for improving the
-        // offload support checking.
-        return DeviceInfoUtils.compareMajorMinorVersion(kVersionString, "4.14") >= 0;
+    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))
+                || current.isInRange(new KVersion(4, 19, 176), new KVersion(5, 4, 0))
+                || current.isAtLeast(new KVersion(5, 4, 98));
     }
 
     @Test
+    public void testIsUdpOffloadSupportedByKernel() throws Exception {
+        assertFalse(isUdpOffloadSupportedByKernel("4.14.221"));
+        assertTrue(isUdpOffloadSupportedByKernel("4.14.222"));
+        assertTrue(isUdpOffloadSupportedByKernel("4.16.0"));
+        assertTrue(isUdpOffloadSupportedByKernel("4.18.0"));
+        assertFalse(isUdpOffloadSupportedByKernel("4.19.0"));
+
+        assertFalse(isUdpOffloadSupportedByKernel("4.19.175"));
+        assertTrue(isUdpOffloadSupportedByKernel("4.19.176"));
+        assertTrue(isUdpOffloadSupportedByKernel("5.2.0"));
+        assertTrue(isUdpOffloadSupportedByKernel("5.3.0"));
+        assertFalse(isUdpOffloadSupportedByKernel("5.4.0"));
+
+        assertFalse(isUdpOffloadSupportedByKernel("5.4.97"));
+        assertTrue(isUdpOffloadSupportedByKernel("5.4.98"));
+        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.
+    @Test
     @IgnoreUpTo(Build.VERSION_CODES.R)
     public void testTetherUdpV4AfterR() throws Exception {
         initializeTethering();
-        boolean usingBpf = isUdpOffloadSupportedByKernel();
+        final String kernelVersion = VintfRuntimeInfo.getKernelVersion();
+        boolean usingBpf = isUdpOffloadSupportedByKernel(kernelVersion);
         if (!usingBpf) {
             Log.i(TAG, "testTetherUdpV4AfterR will skip BPF offload test for kernel "
-                    + VintfRuntimeInfo.getKernelVersion());
+                    + kernelVersion);
         }
         runUdp4Test(new TetheringTester(mDownstreamReader), new RemoteResponder(mUpstreamReader),
                 usingBpf);
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
index e9716b3..8ef0c76 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
@@ -26,6 +26,7 @@
 import static android.net.RouteInfo.RTN_UNICAST;
 import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
 
+import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
 import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
 import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_IFACE;
 import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_UID;
@@ -668,7 +669,7 @@
 
         if (isAtLeastT()) {
             mTetherStatsProviderCb.expectNotifyLimitReached();
-        } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.S) {
+        } else if (isAtLeastS()) {
             mTetherStatsProviderCb.expectNotifyWarningOrLimitReached();
         } else {
             mTetherStatsProviderCb.expectNotifyLimitReached();
diff --git a/bpf_progs/block.c b/bpf_progs/block.c
index 601b932..f2a3e62 100644
--- a/bpf_progs/block.c
+++ b/bpf_progs/block.c
@@ -19,8 +19,8 @@
 #include <netinet/in.h>
 #include <stdint.h>
 
-// The resulting .o needs to load on the Android T bpfloader v0.12+
-#define BPFLOADER_MIN_VER 12u
+// The resulting .o needs to load on the Android T beta 3 bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_T_BETA3_VERSION
 
 #include "bpf_helpers.h"
 
diff --git a/bpf_progs/bpf_shared.h b/bpf_progs/bpf_shared.h
index 14fcdd6..634fbf4 100644
--- a/bpf_progs/bpf_shared.h
+++ b/bpf_progs/bpf_shared.h
@@ -147,9 +147,9 @@
     SELECT_MAP_B,
 };
 
-// TODO: change the configuration object from an 8-bit bitmask to an object with clearer
+// TODO: change the configuration object from a bitmask to an object with clearer
 // semantics, like a struct.
-typedef uint8_t BpfConfig;
+typedef uint32_t BpfConfig;
 static const BpfConfig DEFAULT_CONFIG = 0;
 
 typedef struct {
@@ -160,7 +160,9 @@
 } UidOwnerValue;
 STRUCT_SIZE(UidOwnerValue, 2 * 4);  // 8
 
+// Entry in the configuration map that stores which UID rules are enabled.
 #define UID_RULES_CONFIGURATION_KEY 1
+// Entry in the configuration map that stores which stats map is currently in use.
 #define CURRENT_STATS_MAP_CONFIGURATION_KEY 2
 
 #define BPF_CLATD_PATH "/sys/fs/bpf/net_shared/"
diff --git a/bpf_progs/clatd.c b/bpf_progs/clatd.c
index 87795f5..c5b8555 100644
--- a/bpf_progs/clatd.c
+++ b/bpf_progs/clatd.c
@@ -30,8 +30,8 @@
 #define __kernel_udphdr udphdr
 #include <linux/udp.h>
 
-// The resulting .o needs to load on the Android T bpfloader v0.12+
-#define BPFLOADER_MIN_VER 12u
+// The resulting .o needs to load on the Android T beta 3 bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_T_BETA3_VERSION
 
 #include "bpf_helpers.h"
 #include "bpf_net_helpers.h"
diff --git a/bpf_progs/dscp_policy.c b/bpf_progs/dscp_policy.c
index 7211f2b..538a9e4 100644
--- a/bpf_progs/dscp_policy.c
+++ b/bpf_progs/dscp_policy.c
@@ -27,8 +27,8 @@
 #include <netinet/udp.h>
 #include <string.h>
 
-// The resulting .o needs to load on the Android T bpfloader v0.12+
-#define BPFLOADER_MIN_VER 12u
+// The resulting .o needs to load on the Android T beta 3 bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_T_BETA3_VERSION
 
 #include "bpf_helpers.h"
 #include "dscp_policy.h"
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index d019257..2a63e81 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-// The resulting .o needs to load on the Android T Beta 3 bpfloader v0.13+
-#define BPFLOADER_MIN_VER 13u
+// The resulting .o needs to load on the Android T Beta 3 bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_T_BETA3_VERSION
 
 #include <bpf_helpers.h>
 #include <linux/bpf.h>
@@ -62,7 +62,7 @@
 DEFINE_BPF_MAP_GRW(stats_map_B, HASH, StatsKey, StatsValue, STATS_MAP_SIZE, AID_NET_BW_ACCT)
 DEFINE_BPF_MAP_GRW(iface_stats_map, HASH, uint32_t, StatsValue, IFACE_STATS_MAP_SIZE,
                    AID_NET_BW_ACCT)
-DEFINE_BPF_MAP_GRW(configuration_map, HASH, uint32_t, uint8_t, CONFIGURATION_MAP_SIZE,
+DEFINE_BPF_MAP_GRW(configuration_map, HASH, uint32_t, uint32_t, CONFIGURATION_MAP_SIZE,
                    AID_NET_BW_ACCT)
 DEFINE_BPF_MAP_GRW(uid_owner_map, HASH, uint32_t, UidOwnerValue, UID_OWNER_MAP_SIZE,
                    AID_NET_BW_ACCT)
@@ -234,7 +234,7 @@
 }
 
 static __always_inline inline void update_stats_with_config(struct __sk_buff* skb, int direction,
-                                                            StatsKey* key, uint8_t selectedMap) {
+                                                            StatsKey* key, uint32_t selectedMap) {
     if (selectedMap == SELECT_MAP_A) {
         update_stats_map_A(skb, direction, key);
     } else if (selectedMap == SELECT_MAP_B) {
@@ -286,7 +286,7 @@
     if (counterSet) key.counterSet = (uint32_t)*counterSet;
 
     uint32_t mapSettingKey = CURRENT_STATS_MAP_CONFIGURATION_KEY;
-    uint8_t* selectedMap = bpf_configuration_map_lookup_elem(&mapSettingKey);
+    uint32_t* selectedMap = bpf_configuration_map_lookup_elem(&mapSettingKey);
 
     // Use asm("%0 &= 1" : "+r"(match)) before return match,
     // to help kernel's bpf verifier, so that it can be 100% certain
diff --git a/bpf_progs/offload.c b/bpf_progs/offload.c
index 896bc09..2ec0792 100644
--- a/bpf_progs/offload.c
+++ b/bpf_progs/offload.c
@@ -24,8 +24,8 @@
 #define __kernel_udphdr udphdr
 #include <linux/udp.h>
 
-// The resulting .o needs to load on the Android S bpfloader v0.2
-#define BPFLOADER_MIN_VER 2u
+// The resulting .o needs to load on the Android S bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_S_VERSION
 
 #include "bpf_helpers.h"
 #include "bpf_net_helpers.h"
diff --git a/bpf_progs/test.c b/bpf_progs/test.c
index c9c73f1..f2fcc8c 100644
--- a/bpf_progs/test.c
+++ b/bpf_progs/test.c
@@ -18,8 +18,8 @@
 #include <linux/in.h>
 #include <linux/ip.h>
 
-// The resulting .o needs to load on the Android S bpfloader v0.2
-#define BPFLOADER_MIN_VER 2u
+// The resulting .o needs to load on the Android S bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_S_VERSION
 
 #include "bpf_helpers.h"
 #include "bpf_net_helpers.h"
diff --git a/framework-t/src/android/net/EthernetManager.java b/framework-t/src/android/net/EthernetManager.java
index 886d194..b8070f0 100644
--- a/framework-t/src/android/net/EthernetManager.java
+++ b/framework-t/src/android/net/EthernetManager.java
@@ -22,13 +22,11 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.os.Build;
 import android.os.OutcomeReceiver;
 import android.os.RemoteException;
@@ -573,7 +571,6 @@
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
             android.Manifest.permission.NETWORK_STACK,
             android.Manifest.permission.MANAGE_ETHERNET_NETWORKS})
-    @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
     public void enableInterface(
             @NonNull String iface,
             @Nullable @CallbackExecutor Executor executor,
@@ -582,7 +579,7 @@
         final NetworkInterfaceOutcomeReceiver proxy = makeNetworkInterfaceOutcomeReceiver(
                 executor, callback);
         try {
-            mService.connectNetwork(iface, proxy);
+            mService.enableInterface(iface, proxy);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -610,7 +607,6 @@
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
             android.Manifest.permission.NETWORK_STACK,
             android.Manifest.permission.MANAGE_ETHERNET_NETWORKS})
-    @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
     public void disableInterface(
             @NonNull String iface,
             @Nullable @CallbackExecutor Executor executor,
@@ -619,7 +615,7 @@
         final NetworkInterfaceOutcomeReceiver proxy = makeNetworkInterfaceOutcomeReceiver(
                 executor, callback);
         try {
-            mService.disconnectNetwork(iface, proxy);
+            mService.disableInterface(iface, proxy);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/framework-t/src/android/net/IEthernetManager.aidl b/framework-t/src/android/net/IEthernetManager.aidl
index 42e4c1a..c1efc29 100644
--- a/framework-t/src/android/net/IEthernetManager.aidl
+++ b/framework-t/src/android/net/IEthernetManager.aidl
@@ -43,8 +43,8 @@
     void releaseTetheredInterface(in ITetheredInterfaceCallback callback);
     void updateConfiguration(String iface, in EthernetNetworkUpdateRequest request,
         in INetworkInterfaceOutcomeReceiver listener);
-    void connectNetwork(String iface, in INetworkInterfaceOutcomeReceiver listener);
-    void disconnectNetwork(String iface, in INetworkInterfaceOutcomeReceiver listener);
+    void enableInterface(String iface, in INetworkInterfaceOutcomeReceiver listener);
+    void disableInterface(String iface, in INetworkInterfaceOutcomeReceiver listener);
     void setEthernetEnabled(boolean enabled);
     List<String> getInterfaceList();
 }
diff --git a/framework/src/android/net/NetworkRequest.java b/framework/src/android/net/NetworkRequest.java
index 6245e07..4f9d845 100644
--- a/framework/src/android/net/NetworkRequest.java
+++ b/framework/src/android/net/NetworkRequest.java
@@ -220,7 +220,7 @@
          */
         public Builder(@NonNull final NetworkRequest request) {
             Objects.requireNonNull(request);
-            mNetworkCapabilities = new NetworkCapabilities(request.networkCapabilities);
+            mNetworkCapabilities = request.networkCapabilities;
             // If the caller constructed the builder from a request, it means the user
             // might explicitly want the capabilities from the request. Thus, the NOT_VCN_MANAGED
             // capabilities should not be touched later.
diff --git a/netd/Android.bp b/netd/Android.bp
index 5ac02d3..c731b8b 100644
--- a/netd/Android.bp
+++ b/netd/Android.bp
@@ -55,7 +55,8 @@
 cc_test {
     name: "netd_updatable_unit_test",
     defaults: ["netd_defaults"],
-    test_suites: ["general-tests"],
+    test_suites: ["general-tests", "mts-tethering"],
+    test_config_template: ":net_native_test_config_template",
     require_root: true,  // required by setrlimitForTest()
     header_libs: [
         "bpf_connectivity_headers",
@@ -72,6 +73,7 @@
         "liblog",
         "libnetdutils",
     ],
+    compile_multilib: "both",
     multilib: {
         lib32: {
             suffix: "32",
diff --git a/netd/BpfHandler.h b/netd/BpfHandler.h
index 2ede1c1..05b9ebc 100644
--- a/netd/BpfHandler.h
+++ b/netd/BpfHandler.h
@@ -62,7 +62,7 @@
     BpfMap<uint64_t, UidTagValue> mCookieTagMap;
     BpfMap<StatsKey, StatsValue> mStatsMapA;
     BpfMap<StatsKey, StatsValue> mStatsMapB;
-    BpfMap<uint32_t, uint8_t> mConfigurationMap;
+    BpfMap<uint32_t, uint32_t> mConfigurationMap;
     BpfMap<uint32_t, uint8_t> mUidPermissionMap;
 
     std::mutex mMutex;
diff --git a/netd/BpfHandlerTest.cpp b/netd/BpfHandlerTest.cpp
index cd6b565..12ae916 100644
--- a/netd/BpfHandlerTest.cpp
+++ b/netd/BpfHandlerTest.cpp
@@ -48,7 +48,7 @@
     BpfHandler mBh;
     BpfMap<uint64_t, UidTagValue> mFakeCookieTagMap;
     BpfMap<StatsKey, StatsValue> mFakeStatsMapA;
-    BpfMap<uint32_t, uint8_t> mFakeConfigurationMap;
+    BpfMap<uint32_t, uint32_t> mFakeConfigurationMap;
     BpfMap<uint32_t, uint8_t> mFakeUidPermissionMap;
 
     void SetUp() {
diff --git a/service-t/native/libs/libnetworkstats/Android.bp b/service-t/native/libs/libnetworkstats/Android.bp
index bf56fd5..5b3d314 100644
--- a/service-t/native/libs/libnetworkstats/Android.bp
+++ b/service-t/native/libs/libnetworkstats/Android.bp
@@ -48,7 +48,8 @@
 
 cc_test {
     name: "libnetworkstats_test",
-    test_suites: ["general-tests"],
+    test_suites: ["general-tests", "mts-tethering"],
+    test_config_template: ":net_native_test_config_template",
     require_root: true,  // required by setrlimitForTest()
     header_libs: ["bpf_connectivity_headers"],
     srcs: [
@@ -68,4 +69,13 @@
         "libbase",
         "liblog",
     ],
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
 }
diff --git a/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp b/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
index 6c7a15e..c67821f 100644
--- a/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
+++ b/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
@@ -193,7 +193,7 @@
         return ret;
     }
 
-    BpfMapRO<uint32_t, uint8_t> configurationMap(CONFIGURATION_MAP_PATH);
+    BpfMapRO<uint32_t, uint32_t> configurationMap(CONFIGURATION_MAP_PATH);
     if (!configurationMap.isValid()) {
         int ret = -errno;
         ALOGE("get configuration map fd failed: %s", strerror(errno));
diff --git a/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java b/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java
index 5e830ad..71d3e4f 100644
--- a/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java
+++ b/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java
@@ -22,11 +22,11 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.net.EthernetNetworkUpdateRequest;
 import android.net.IEthernetManager;
 import android.net.IEthernetServiceListener;
 import android.net.INetworkInterfaceOutcomeReceiver;
 import android.net.ITetheredInterfaceCallback;
-import android.net.EthernetNetworkUpdateRequest;
 import android.net.IpConfiguration;
 import android.net.NetworkCapabilities;
 import android.os.Binder;
@@ -260,27 +260,27 @@
     }
 
     @Override
-    public void connectNetwork(@NonNull final String iface,
+    public void enableInterface(@NonNull final String iface,
             @Nullable final INetworkInterfaceOutcomeReceiver listener) {
-        Log.i(TAG, "connectNetwork called with: iface=" + iface + ", listener=" + listener);
+        Log.i(TAG, "enableInterface called with: iface=" + iface + ", listener=" + listener);
         Objects.requireNonNull(iface);
         throwIfEthernetNotStarted();
 
-        enforceAdminPermission(iface, true, "connectNetwork()");
+        enforceAdminPermission(iface, false, "enableInterface()");
 
-        mTracker.connectNetwork(iface, listener);
+        mTracker.enableInterface(iface, listener);
     }
 
     @Override
-    public void disconnectNetwork(@NonNull final String iface,
+    public void disableInterface(@NonNull final String iface,
             @Nullable final INetworkInterfaceOutcomeReceiver listener) {
-        Log.i(TAG, "disconnectNetwork called with: iface=" + iface + ", listener=" + listener);
+        Log.i(TAG, "disableInterface called with: iface=" + iface + ", listener=" + listener);
         Objects.requireNonNull(iface);
         throwIfEthernetNotStarted();
 
-        enforceAdminPermission(iface, true, "connectNetwork()");
+        enforceAdminPermission(iface, false, "disableInterface()");
 
-        mTracker.disconnectNetwork(iface, listener);
+        mTracker.disableInterface(iface, listener);
     }
 
     @Override
diff --git a/service-t/src/com/android/server/ethernet/EthernetTracker.java b/service-t/src/com/android/server/ethernet/EthernetTracker.java
index e5f1ea5..c8a0412 100644
--- a/service-t/src/com/android/server/ethernet/EthernetTracker.java
+++ b/service-t/src/com/android/server/ethernet/EthernetTracker.java
@@ -285,13 +285,13 @@
     }
 
     @VisibleForTesting(visibility = PACKAGE)
-    protected void connectNetwork(@NonNull final String iface,
+    protected void enableInterface(@NonNull final String iface,
             @Nullable final INetworkInterfaceOutcomeReceiver listener) {
         mHandler.post(() -> updateInterfaceState(iface, true, listener));
     }
 
     @VisibleForTesting(visibility = PACKAGE)
-    protected void disconnectNetwork(@NonNull final String iface,
+    protected void disableInterface(@NonNull final String iface,
             @Nullable final INetworkInterfaceOutcomeReceiver listener) {
         mHandler.post(() -> updateInterfaceState(iface, false, listener));
     }
diff --git a/service-t/src/com/android/server/net/NetworkStatsObservers.java b/service-t/src/com/android/server/net/NetworkStatsObservers.java
index df4e7f5..1cd670a 100644
--- a/service-t/src/com/android/server/net/NetworkStatsObservers.java
+++ b/service-t/src/com/android/server/net/NetworkStatsObservers.java
@@ -198,7 +198,7 @@
 
         if (LOG) Log.d(TAG, "Unregistering " + requestInfo);
         mDataUsageRequests.remove(request.requestId);
-        mDataUsageRequestsPerUid.decrementCountOrThrow(callingUid);
+        mDataUsageRequestsPerUid.decrementCountOrThrow(requestInfo.mCallingUid);
         requestInfo.unlinkDeathRecipient();
         requestInfo.callCallback(NetworkStatsManager.CALLBACK_RELEASED);
     }
diff --git a/service/jni/com_android_server_BpfNetMaps.cpp b/service/jni/com_android_server_BpfNetMaps.cpp
index 7b1f59c..bc70c93 100644
--- a/service/jni/com_android_server_BpfNetMaps.cpp
+++ b/service/jni/com_android_server_BpfNetMaps.cpp
@@ -39,152 +39,126 @@
 
 namespace android {
 
-static void native_init(JNIEnv* env, jobject clazz) {
+#define CHECK_LOG(status) \
+  do { \
+    if (!isOk(status)) \
+      ALOGE("%s failed, error code = %d", __func__, status.code()); \
+  } while (0)
+
+static void native_init(JNIEnv* env, jclass clazz) {
   Status status = mTc.start();
-   if (!isOk(status)) {
-    ALOGE("%s failed, error code = %d", __func__, status.code());
-  }
+  CHECK_LOG(status);
 }
 
-static jint native_addNaughtyApp(JNIEnv* env, jobject clazz, jint uid) {
+static jint native_addNaughtyApp(JNIEnv* env, jobject self, jint uid) {
   const uint32_t appUids = static_cast<uint32_t>(abs(uid));
   Status status = mTc.updateUidOwnerMap(appUids, PENALTY_BOX_MATCH,
       TrafficController::IptOp::IptOpInsert);
-  if (!isOk(status)) {
-    ALOGE("%s failed, error code = %d", __func__, status.code());
-  }
+  CHECK_LOG(status);
   return (jint)status.code();
 }
 
-static jint native_removeNaughtyApp(JNIEnv* env, jobject clazz, jint uid) {
+static jint native_removeNaughtyApp(JNIEnv* env, jobject self, jint uid) {
   const uint32_t appUids = static_cast<uint32_t>(abs(uid));
   Status status = mTc.updateUidOwnerMap(appUids, PENALTY_BOX_MATCH,
       TrafficController::IptOp::IptOpDelete);
-  if (!isOk(status)) {
-    ALOGE("%s failed, error code = %d", __func__, status.code());
-  }
+  CHECK_LOG(status);
   return (jint)status.code();
 }
 
-static jint native_addNiceApp(JNIEnv* env, jobject clazz, jint uid) {
+static jint native_addNiceApp(JNIEnv* env, jobject self, jint uid) {
   const uint32_t appUids = static_cast<uint32_t>(abs(uid));
   Status status = mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH,
       TrafficController::IptOp::IptOpInsert);
-  if (!isOk(status)) {
-    ALOGE("%s failed, error code = %d", __func__, status.code());
-  }
+  CHECK_LOG(status);
   return (jint)status.code();
 }
 
-static jint native_removeNiceApp(JNIEnv* env, jobject clazz, jint uid) {
+static jint native_removeNiceApp(JNIEnv* env, jobject self, jint uid) {
   const uint32_t appUids = static_cast<uint32_t>(abs(uid));
   Status status = mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH,
       TrafficController::IptOp::IptOpDelete);
-  if (!isOk(status)) {
-    ALOGD("%s failed, error code = %d", __func__, status.code());
-  }
+  CHECK_LOG(status);
   return (jint)status.code();
 }
 
-static jint native_setChildChain(JNIEnv* env, jobject clazz, jint childChain, jboolean enable) {
+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);
-  }
+  if (res) ALOGE("%s failed, error code = %d", __func__, res);
   return (jint)res;
 }
 
-static jint native_replaceUidChain(JNIEnv* env, jobject clazz, jstring name, jboolean isAllowlist,
-                                jintArray jUids) {
+static jint native_replaceUidChain(JNIEnv* env, jobject self, jstring name, jboolean isAllowlist,
+                                   jintArray jUids) {
     const ScopedUtfChars chainNameUtf8(env, name);
-    if (chainNameUtf8.c_str() == nullptr) {
-        return -EINVAL;
-    }
+    if (chainNameUtf8.c_str() == nullptr) return -EINVAL;
     const std::string chainName(chainNameUtf8.c_str());
 
     ScopedIntArrayRO uids(env, jUids);
-    if (uids.get() == nullptr) {
-        return -EINVAL;
-    }
+    if (uids.get() == nullptr) return -EINVAL;
 
     size_t size = uids.size();
     static_assert(sizeof(*(uids.get())) == sizeof(int32_t));
     std::vector<int32_t> data ((int32_t *)&uids[0], (int32_t*)&uids[size]);
     int res = mTc.replaceUidOwnerMap(chainName, isAllowlist, data);
-    if (res) {
-      ALOGE("%s failed, error code = %d", __func__, res);
-    }
+    if (res) ALOGE("%s failed, error code = %d", __func__, res);
     return (jint)res;
 }
 
-static jint native_setUidRule(JNIEnv* env, jobject clazz, jint childChain, jint uid,
-                          jint firewallRule) {
+static jint native_setUidRule(JNIEnv* env, jobject self, jint childChain, jint uid,
+                              jint firewallRule) {
     auto chain = static_cast<ChildChain>(childChain);
     auto rule = static_cast<FirewallRule>(firewallRule);
     FirewallType fType = mTc.getFirewallType(chain);
 
     int res = mTc.changeUidOwnerRule(chain, uid, rule, fType);
-    if (res) {
-      ALOGE("%s failed, error code = %d", __func__, res);
-    }
+    if (res) ALOGE("%s failed, error code = %d", __func__, res);
     return (jint)res;
 }
 
-static jint native_addUidInterfaceRules(JNIEnv* env, jobject clazz, jstring ifName,
-                                    jintArray jUids) {
+static jint native_addUidInterfaceRules(JNIEnv* env, jobject self, jstring ifName,
+                                        jintArray jUids) {
     // Null ifName is a wildcard to allow apps to receive packets on all interfaces and ifIndex is
     // set to 0.
-    int ifIndex;
+    int ifIndex = 0;
     if (ifName != nullptr) {
         const ScopedUtfChars ifNameUtf8(env, ifName);
         const std::string interfaceName(ifNameUtf8.c_str());
         ifIndex = if_nametoindex(interfaceName.c_str());
-    } else {
-        ifIndex = 0;
     }
 
     ScopedIntArrayRO uids(env, jUids);
-    if (uids.get() == nullptr) {
-        return -EINVAL;
-    }
+    if (uids.get() == nullptr) return -EINVAL;
 
     size_t size = uids.size();
     static_assert(sizeof(*(uids.get())) == sizeof(int32_t));
     std::vector<int32_t> data ((int32_t *)&uids[0], (int32_t*)&uids[size]);
     Status status = mTc.addUidInterfaceRules(ifIndex, data);
-    if (!isOk(status)) {
-        ALOGE("%s failed, error code = %d", __func__, status.code());
-    }
+    CHECK_LOG(status);
     return (jint)status.code();
 }
 
-static jint native_removeUidInterfaceRules(JNIEnv* env, jobject clazz, jintArray jUids) {
+static jint native_removeUidInterfaceRules(JNIEnv* env, jobject self, jintArray jUids) {
     ScopedIntArrayRO uids(env, jUids);
-    if (uids.get() == nullptr) {
-        return -EINVAL;
-    }
+    if (uids.get() == nullptr) return -EINVAL;
 
     size_t size = uids.size();
     static_assert(sizeof(*(uids.get())) == sizeof(int32_t));
     std::vector<int32_t> data ((int32_t *)&uids[0], (int32_t*)&uids[size]);
     Status status = mTc.removeUidInterfaceRules(data);
-    if (!isOk(status)) {
-        ALOGE("%s failed, error code = %d", __func__, status.code());
-    }
+    CHECK_LOG(status);
     return (jint)status.code();
 }
 
-static jint native_swapActiveStatsMap(JNIEnv* env, jobject clazz) {
+static jint native_swapActiveStatsMap(JNIEnv* env, jobject self) {
     Status status = mTc.swapActiveStatsMap();
-    if (!isOk(status)) {
-        ALOGD("%s failed, error code = %d", __func__, status.code());
-    }
+    CHECK_LOG(status);
     return (jint)status.code();
 }
 
-static void native_setPermissionForUids(JNIEnv* env, jobject clazz, jint permission,
-                                      jintArray jUids) {
+static void native_setPermissionForUids(JNIEnv* env, jobject self, jint permission,
+                                        jintArray jUids) {
     ScopedIntArrayRO uids(env, jUids);
     if (uids.get() == nullptr) return;
 
@@ -194,7 +168,7 @@
     mTc.setPermissionForUids(permission, data);
 }
 
-static void native_dump(JNIEnv* env, jobject clazz, jobject javaFd, jboolean verbose) {
+static void native_dump(JNIEnv* env, jobject self, jobject javaFd, jboolean verbose) {
     int fd = netjniutils::GetNativeFileDescriptor(env, javaFd);
     if (fd < 0) {
         jniThrowExceptionFmt(env, "java/io/IOException", "Invalid file descriptor");
@@ -239,9 +213,8 @@
 // clang-format on
 
 int register_com_android_server_BpfNetMaps(JNIEnv* env) {
-    return jniRegisterNativeMethods(env,
-    "com/android/server/BpfNetMaps",
-    gMethods, NELEM(gMethods));
+    return jniRegisterNativeMethods(env, "com/android/server/BpfNetMaps",
+                                    gMethods, NELEM(gMethods));
 }
 
 }; // namespace android
diff --git a/service/native/Android.bp b/service/native/Android.bp
index cb26bc3..697fcbd 100644
--- a/service/native/Android.bp
+++ b/service/native/Android.bp
@@ -52,7 +52,8 @@
 
 cc_test {
     name: "traffic_controller_unit_test",
-    test_suites: ["general-tests"],
+    test_suites: ["general-tests", "mts-tethering"],
+    test_config_template: ":net_native_test_config_template",
     require_root: true,
     local_include_dirs: ["include"],
     header_libs: [
@@ -71,4 +72,13 @@
         "libnetd_updatable",
         "netd_aidl_interface-lateststable-ndk",
     ],
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
 }
diff --git a/service/native/TrafficController.cpp b/service/native/TrafficController.cpp
index a9ede6a..55db393 100644
--- a/service/native/TrafficController.cpp
+++ b/service/native/TrafficController.cpp
@@ -455,15 +455,15 @@
 int TrafficController::toggleUidOwnerMap(ChildChain chain, bool enable) {
     std::lock_guard guard(mMutex);
     uint32_t key = UID_RULES_CONFIGURATION_KEY;
-    auto oldConfiguration = mConfigurationMap.readValue(key);
-    if (!oldConfiguration.ok()) {
+    auto oldConfigure = mConfigurationMap.readValue(key);
+    if (!oldConfigure.ok()) {
         ALOGE("Cannot read the old configuration from map: %s",
-              oldConfiguration.error().message().c_str());
-        return -oldConfiguration.error().code();
+              oldConfigure.error().message().c_str());
+        return -oldConfigure.error().code();
     }
     Status res;
     BpfConfig newConfiguration;
-    uint8_t match;
+    uint32_t match;
     switch (chain) {
         case DOZABLE:
             match = DOZABLE_MATCH;
@@ -484,7 +484,7 @@
             return -EINVAL;
     }
     newConfiguration =
-            enable ? (oldConfiguration.value() | match) : (oldConfiguration.value() & (~match));
+            enable ? (oldConfigure.value() | match) : (oldConfigure.value() & (~match));
     res = mConfigurationMap.writeValue(key, newConfiguration, BPF_EXIST);
     if (!isOk(res)) {
         ALOGE("Failed to toggleUidOwnerMap(%d): %s", chain, res.msg().c_str());
@@ -496,17 +496,17 @@
     std::lock_guard guard(mMutex);
 
     uint32_t key = CURRENT_STATS_MAP_CONFIGURATION_KEY;
-    auto oldConfiguration = mConfigurationMap.readValue(key);
-    if (!oldConfiguration.ok()) {
+    auto oldConfigure = mConfigurationMap.readValue(key);
+    if (!oldConfigure.ok()) {
         ALOGE("Cannot read the old configuration from map: %s",
-              oldConfiguration.error().message().c_str());
-        return Status(oldConfiguration.error().code(), oldConfiguration.error().message());
+              oldConfigure.error().message().c_str());
+        return Status(oldConfigure.error().code(), oldConfigure.error().message());
     }
 
     // Write to the configuration map to inform the kernel eBPF program to switch
     // from using one map to the other. Use flag BPF_EXIST here since the map should
     // be already populated in initMaps.
-    uint8_t newConfigure = (oldConfiguration.value() == SELECT_MAP_A) ? SELECT_MAP_B : SELECT_MAP_A;
+    uint32_t newConfigure = (oldConfigure.value() == SELECT_MAP_A) ? SELECT_MAP_B : SELECT_MAP_A;
     auto res = mConfigurationMap.writeValue(CURRENT_STATS_MAP_CONFIGURATION_KEY, newConfigure,
                                             BPF_EXIST);
     if (!res.ok()) {
diff --git a/service/native/TrafficControllerTest.cpp b/service/native/TrafficControllerTest.cpp
index 3f13532..96bd57c 100644
--- a/service/native/TrafficControllerTest.cpp
+++ b/service/native/TrafficControllerTest.cpp
@@ -65,7 +65,7 @@
     BpfMap<uint64_t, UidTagValue> mFakeCookieTagMap;
     BpfMap<uint32_t, StatsValue> mFakeAppUidStatsMap;
     BpfMap<StatsKey, StatsValue> mFakeStatsMapA;
-    BpfMap<uint32_t, uint8_t> mFakeConfigurationMap;
+    BpfMap<uint32_t, uint32_t> mFakeConfigurationMap;
     BpfMap<uint32_t, UidOwnerValue> mFakeUidOwnerMap;
     BpfMap<uint32_t, uint8_t> mFakeUidPermissionMap;
 
diff --git a/service/native/include/TrafficController.h b/service/native/include/TrafficController.h
index 79e75ac..d3d52e2 100644
--- a/service/native/include/TrafficController.h
+++ b/service/native/include/TrafficController.h
@@ -155,7 +155,7 @@
      *    Userspace can do scraping and cleaning job on the other one depending on the
      *    current configs.
      */
-    bpf::BpfMap<uint32_t, uint8_t> mConfigurationMap GUARDED_BY(mMutex);
+    bpf::BpfMap<uint32_t, uint32_t> mConfigurationMap GUARDED_BY(mMutex);
 
     /*
      * mUidOwnerMap: Store uids that are used for bandwidth control uid match.
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 986b108..e55678c 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -3872,7 +3872,6 @@
             }
 
             final boolean wasValidated = nai.lastValidated;
-            final boolean wasDefault = isDefaultNetwork(nai);
             final boolean wasPartial = nai.partialConnectivity;
             nai.partialConnectivity = ((testResult & NETWORK_VALIDATION_RESULT_PARTIAL) != 0);
             final boolean partialConnectivityChanged =
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index e979a3b..4d85d72 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -94,7 +94,7 @@
     ],
     jni_uses_sdk_apis: true,
     min_sdk_version: "29",
-    target_sdk_version: "30",
+    target_sdk_version: "33",
     test_suites: [
         "general-tests",
         "mts-dnsresolver",
diff --git a/tests/cts/net/api23Test/Android.bp b/tests/cts/net/api23Test/Android.bp
index 5b37294..9b81a56 100644
--- a/tests/cts/net/api23Test/Android.bp
+++ b/tests/cts/net/api23Test/Android.bp
@@ -51,5 +51,8 @@
         "cts",
         "general-tests",
     ],
-
+    data: [
+        ":CtsNetTestAppForApi23",
+    ],
+    per_testcase_directory: true,
 }
diff --git a/tests/cts/net/src/android/net/cts/EthernetNetworkUpdateRequestTest.java b/tests/cts/net/src/android/net/cts/EthernetNetworkUpdateRequestTest.java
new file mode 100644
index 0000000..c8ee0c7
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/EthernetNetworkUpdateRequestTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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 android.net.cts;
+
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+
+import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertThrows;
+
+import android.annotation.NonNull;
+import android.net.EthernetNetworkUpdateRequest;
+import android.net.IpConfiguration;
+import android.net.NetworkCapabilities;
+import android.net.StaticIpConfiguration;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
+@RunWith(DevSdkIgnoreRunner.class)
+public class EthernetNetworkUpdateRequestTest {
+    private static final NetworkCapabilities DEFAULT_CAPS =
+            new NetworkCapabilities.Builder()
+                    .removeCapability(NET_CAPABILITY_NOT_RESTRICTED).build();
+    private static final StaticIpConfiguration DEFAULT_STATIC_IP_CONFIG =
+            new StaticIpConfiguration.Builder().setDomains("test").build();
+    private static final IpConfiguration DEFAULT_IP_CONFIG =
+            new IpConfiguration.Builder()
+                    .setStaticIpConfiguration(DEFAULT_STATIC_IP_CONFIG).build();
+
+    private EthernetNetworkUpdateRequest createRequest(@NonNull final NetworkCapabilities nc,
+            @NonNull final IpConfiguration ipConfig) {
+        return new EthernetNetworkUpdateRequest.Builder()
+                .setNetworkCapabilities(nc)
+                .setIpConfiguration(ipConfig)
+                .build();
+    }
+
+    @Test
+    public void testGetNetworkCapabilities() {
+        final EthernetNetworkUpdateRequest r = createRequest(DEFAULT_CAPS, DEFAULT_IP_CONFIG);
+        assertEquals(DEFAULT_CAPS, r.getNetworkCapabilities());
+    }
+
+    @Test
+    public void testGetIpConfiguration() {
+        final EthernetNetworkUpdateRequest r = createRequest(DEFAULT_CAPS, DEFAULT_IP_CONFIG);
+        assertEquals(DEFAULT_IP_CONFIG, r.getIpConfiguration());
+    }
+
+    @Test
+    public void testBuilderWithRequest() {
+        final EthernetNetworkUpdateRequest r = createRequest(DEFAULT_CAPS, DEFAULT_IP_CONFIG);
+        final EthernetNetworkUpdateRequest rFromExisting =
+                new EthernetNetworkUpdateRequest.Builder(r).build();
+
+        assertNotSame(r, rFromExisting);
+        assertEquals(r.getIpConfiguration(), rFromExisting.getIpConfiguration());
+        assertEquals(r.getNetworkCapabilities(), rFromExisting.getNetworkCapabilities());
+    }
+
+    @Test
+    public void testNullIpConfigurationAndNetworkCapabilitiesThrows() {
+        assertThrows("Should not be able to build with null ip config and network capabilities.",
+                IllegalStateException.class,
+                () -> createRequest(null, null));
+    }
+}
diff --git a/tests/native/Android.bp b/tests/native/Android.bp
index a8d908a..7d43aa8 100644
--- a/tests/native/Android.bp
+++ b/tests/native/Android.bp
@@ -31,3 +31,10 @@
     ],
     compile_multilib: "first",
 }
+
+filegroup {
+    name: "net_native_test_config_template",
+    srcs: [
+        "NetNativeTestConfigTemplate.xml",
+    ],
+}
diff --git a/tests/native/NetNativeTestConfigTemplate.xml b/tests/native/NetNativeTestConfigTemplate.xml
new file mode 100644
index 0000000..b71e9aa
--- /dev/null
+++ b/tests/native/NetNativeTestConfigTemplate.xml
@@ -0,0 +1,31 @@
+<!-- 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.
+-->
+<configuration description="Configuration for {MODULE} tests">
+    <option name="test-suite-tag" value="mts" />
+    <option name="config-descriptor:metadata" key="mainline-param" value="CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex" />
+    <!-- Only run tests if the device under test is SDK version 33 (Android 13) or above. -->
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk33ModuleController" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="{MODULE}->/data/local/tmp/{MODULE}" />
+        <option name="append-bitness" value="true" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="{MODULE}" />
+    </test>
+</configuration>
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index 5b926de..e6cf22a 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -54,42 +54,21 @@
 filegroup {
     name: "non-connectivity-module-test",
     srcs: [
-        "java/android/app/usage/*.java",
-        "java/android/net/EthernetNetworkUpdateRequestTest.java",
         "java/android/net/Ikev2VpnProfileTest.java",
         "java/android/net/IpMemoryStoreTest.java",
-        "java/android/net/IpSecAlgorithmTest.java",
-        "java/android/net/IpSecConfigTest.java",
-        "java/android/net/IpSecManagerTest.java",
-        "java/android/net/IpSecTransformTest.java",
-        "java/android/net/KeepalivePacketDataUtilTest.java",
-        "java/android/net/NetworkIdentitySetTest.kt",
-        "java/android/net/NetworkIdentityTest.kt",
-        "java/android/net/NetworkStats*.java",
-        "java/android/net/NetworkTemplateTest.kt",
         "java/android/net/TelephonyNetworkSpecifierTest.java",
         "java/android/net/VpnManagerTest.java",
         "java/android/net/ipmemorystore/*.java",
         "java/android/net/netstats/NetworkStatsDataMigrationUtilsTest.kt",
-        "java/android/net/nsd/*.java",
         "java/com/android/internal/net/NetworkUtilsInternalTest.java",
         "java/com/android/internal/net/VpnProfileTest.java",
-        "java/com/android/server/IpSecServiceParameterizedTest.java",
-        "java/com/android/server/IpSecServiceRefcountedResourceTest.java",
-        "java/com/android/server/IpSecServiceTest.java",
         "java/com/android/server/NetworkManagementServiceTest.java",
-        "java/com/android/server/NsdServiceTest.java",
         "java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java",
         "java/com/android/server/connectivity/IpConnectivityMetricsTest.java",
         "java/com/android/server/connectivity/MultipathPolicyTrackerTest.java",
         "java/com/android/server/connectivity/NetdEventListenerServiceTest.java",
         "java/com/android/server/connectivity/VpnTest.java",
-        "java/com/android/server/ethernet/*.java",
         "java/com/android/server/net/ipmemorystore/*.java",
-        "java/com/android/server/net/BpfInterfaceMapUpdaterTest.java",
-        "java/com/android/server/net/IpConfigStoreTest.java",
-        "java/com/android/server/net/NetworkStats*.java",
-        "java/com/android/server/net/TestableUsageCallback.kt",
     ]
 }
 
diff --git a/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java b/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
index 561e621..b1b76ec 100644
--- a/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
+++ b/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
@@ -54,7 +54,7 @@
 
 @RunWith(DevSdkIgnoreRunner.class)
 @SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 public class NetworkStatsManagerTest {
     private static final String TEST_SUBSCRIBER_ID = "subid";
 
diff --git a/tests/unit/java/android/net/IpSecAlgorithmTest.java b/tests/unit/java/android/net/IpSecAlgorithmTest.java
index c473e82..1482055 100644
--- a/tests/unit/java/android/net/IpSecAlgorithmTest.java
+++ b/tests/unit/java/android/net/IpSecAlgorithmTest.java
@@ -47,7 +47,7 @@
 /** Unit tests for {@link IpSecAlgorithm}. */
 @SmallTest
 @RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 public class IpSecAlgorithmTest {
     private static final byte[] KEY_MATERIAL;
 
diff --git a/tests/unit/java/android/net/IpSecConfigTest.java b/tests/unit/java/android/net/IpSecConfigTest.java
index b87cb48..9f83036 100644
--- a/tests/unit/java/android/net/IpSecConfigTest.java
+++ b/tests/unit/java/android/net/IpSecConfigTest.java
@@ -36,7 +36,7 @@
 /** Unit tests for {@link IpSecConfig}. */
 @SmallTest
 @RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 public class IpSecConfigTest {
 
     @Test
diff --git a/tests/unit/java/android/net/IpSecManagerTest.java b/tests/unit/java/android/net/IpSecManagerTest.java
index cda8eb7..335f539 100644
--- a/tests/unit/java/android/net/IpSecManagerTest.java
+++ b/tests/unit/java/android/net/IpSecManagerTest.java
@@ -52,7 +52,7 @@
 /** Unit tests for {@link IpSecManager}. */
 @SmallTest
 @RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 public class IpSecManagerTest {
 
     private static final int TEST_UDP_ENCAP_PORT = 34567;
diff --git a/tests/unit/java/android/net/IpSecTransformTest.java b/tests/unit/java/android/net/IpSecTransformTest.java
index 81375f1..c1bd719 100644
--- a/tests/unit/java/android/net/IpSecTransformTest.java
+++ b/tests/unit/java/android/net/IpSecTransformTest.java
@@ -32,7 +32,7 @@
 /** Unit tests for {@link IpSecTransform}. */
 @SmallTest
 @RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 public class IpSecTransformTest {
 
     @Test
diff --git a/tests/unit/java/android/net/NetworkIdentityTest.kt b/tests/unit/java/android/net/NetworkIdentityTest.kt
index bf5568d..d84328c 100644
--- a/tests/unit/java/android/net/NetworkIdentityTest.kt
+++ b/tests/unit/java/android/net/NetworkIdentityTest.kt
@@ -47,7 +47,7 @@
 private const val TEST_SUBID2 = 2
 
 @RunWith(DevSdkIgnoreRunner::class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 class NetworkIdentityTest {
     private val mockContext = mock(Context::class.java)
 
diff --git a/tests/unit/java/android/net/NetworkStatsAccessTest.java b/tests/unit/java/android/net/NetworkStatsAccessTest.java
index 97a93ca..a74056b 100644
--- a/tests/unit/java/android/net/NetworkStatsAccessTest.java
+++ b/tests/unit/java/android/net/NetworkStatsAccessTest.java
@@ -19,6 +19,7 @@
 import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.when;
 
 import android.Manifest;
@@ -66,6 +67,10 @@
         when(mContext.getSystemServiceName(DevicePolicyManager.class))
                 .thenReturn(Context.DEVICE_POLICY_SERVICE);
         when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(mDpm);
+        if (mContext.getSystemService(DevicePolicyManager.class) == null) {
+            // Test is using mockito-extended
+            doCallRealMethod().when(mContext).getSystemService(DevicePolicyManager.class);
+        }
 
         setHasCarrierPrivileges(false);
         setIsDeviceOwner(false);
diff --git a/tests/unit/java/android/net/NetworkStatsHistoryTest.java b/tests/unit/java/android/net/NetworkStatsHistoryTest.java
index 26079a2..43e331b 100644
--- a/tests/unit/java/android/net/NetworkStatsHistoryTest.java
+++ b/tests/unit/java/android/net/NetworkStatsHistoryTest.java
@@ -60,7 +60,7 @@
 
 @RunWith(DevSdkIgnoreRunner.class)
 @SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 public class NetworkStatsHistoryTest {
     private static final String TAG = "NetworkStatsHistoryTest";
 
diff --git a/tests/unit/java/android/net/NetworkStatsTest.java b/tests/unit/java/android/net/NetworkStatsTest.java
index b0cc16c..6d79869 100644
--- a/tests/unit/java/android/net/NetworkStatsTest.java
+++ b/tests/unit/java/android/net/NetworkStatsTest.java
@@ -61,7 +61,7 @@
 
 @RunWith(DevSdkIgnoreRunner.class)
 @SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 public class NetworkStatsTest {
 
     private static final String TEST_IFACE = "test0";
diff --git a/tests/unit/java/android/net/NetworkTemplateTest.kt b/tests/unit/java/android/net/NetworkTemplateTest.kt
index abd1825..3e9662d 100644
--- a/tests/unit/java/android/net/NetworkTemplateTest.kt
+++ b/tests/unit/java/android/net/NetworkTemplateTest.kt
@@ -70,7 +70,7 @@
 private const val TEST_WIFI_KEY2 = "wifiKey2"
 
 @RunWith(DevSdkIgnoreRunner::class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 class NetworkTemplateTest {
     private val mockContext = mock(Context::class.java)
     private val mockWifiInfo = mock(WifiInfo::class.java)
diff --git a/tests/unit/java/android/net/nsd/NsdManagerTest.java b/tests/unit/java/android/net/nsd/NsdManagerTest.java
index 30b8fcd..32274bc 100644
--- a/tests/unit/java/android/net/nsd/NsdManagerTest.java
+++ b/tests/unit/java/android/net/nsd/NsdManagerTest.java
@@ -51,7 +51,7 @@
 
 @RunWith(DevSdkIgnoreRunner.class)
 @SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 public class NsdManagerTest {
 
     static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD;
diff --git a/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java b/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java
index e5e7ebc..0354377 100644
--- a/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java
+++ b/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java
@@ -42,7 +42,7 @@
 
 @RunWith(DevSdkIgnoreRunner.class)
 @SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 public class NsdServiceInfoTest {
 
     public final static InetAddress LOCALHOST;
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index f6ccd8a..ef97168 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -9505,7 +9505,7 @@
         b2.expectBroadcast();
     }
 
-    @Test
+    @Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
     public void testLockdownSetFirewallUidRule() throws Exception {
         // For ConnectivityService#setAlwaysOnVpnPackage.
         mServiceContext.setPermission(
@@ -10428,7 +10428,7 @@
     }
 
     @Test
-    public void testLegacyVpnSetInterfaceFilteringRuleWithWildcard() throws Exception {
+    public void testLegacyVpnInterfaceFilteringRule() throws Exception {
         LinkProperties lp = new LinkProperties();
         lp.setInterfaceName("tun0");
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
@@ -10438,29 +10438,34 @@
         mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
         assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
 
-        // A connected Legacy VPN should have interface rules with null interface.
-        // Null Interface is a wildcard and this accepts traffic from all the interfaces.
-        // There are two expected invocations, one during the VPN initial connection,
-        // one during the VPN LinkProperties update.
-        ArgumentCaptor<int[]> uidCaptor = ArgumentCaptor.forClass(int[].class);
-        verify(mBpfNetMaps, times(2)).addUidInterfaceRules(
-                eq(null) /* iface */, uidCaptor.capture());
-        assertContainsExactly(uidCaptor.getAllValues().get(0), APP1_UID, APP2_UID, VPN_UID);
-        assertContainsExactly(uidCaptor.getAllValues().get(1), APP1_UID, APP2_UID, VPN_UID);
-        assertEquals(mService.mPermissionMonitor.getVpnInterfaceUidRanges(null /* iface */),
-                vpnRange);
+        if (SdkLevel.isAtLeastT()) {
+            // On T and above, A connected Legacy VPN should have interface rules with null
+            // interface. Null Interface is a wildcard and this accepts traffic from all the
+            // interfaces. There are two expected invocations, one during the VPN initial
+            // connection, one during the VPN LinkProperties update.
+            ArgumentCaptor<int[]> uidCaptor = ArgumentCaptor.forClass(int[].class);
+            verify(mBpfNetMaps, times(2)).addUidInterfaceRules(
+                    eq(null) /* iface */, uidCaptor.capture());
+            assertContainsExactly(uidCaptor.getAllValues().get(0), APP1_UID, APP2_UID, VPN_UID);
+            assertContainsExactly(uidCaptor.getAllValues().get(1), APP1_UID, APP2_UID, VPN_UID);
+            assertEquals(mService.mPermissionMonitor.getVpnInterfaceUidRanges(null /* iface */),
+                    vpnRange);
 
-        mMockVpn.disconnect();
-        waitForIdle();
+            mMockVpn.disconnect();
+            waitForIdle();
 
-        // Disconnected VPN should have interface rules removed
-        verify(mBpfNetMaps).removeUidInterfaceRules(uidCaptor.capture());
-        assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID, VPN_UID);
-        assertNull(mService.mPermissionMonitor.getVpnInterfaceUidRanges(null /* iface */));
+            // Disconnected VPN should have interface rules removed
+            verify(mBpfNetMaps).removeUidInterfaceRules(uidCaptor.capture());
+            assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID, VPN_UID);
+            assertNull(mService.mPermissionMonitor.getVpnInterfaceUidRanges(null /* iface */));
+        } else {
+            // Before T, Legacy VPN should not have interface rules.
+            verify(mBpfNetMaps, never()).addUidInterfaceRules(any(), any());
+        }
     }
 
     @Test
-    public void testLocalIpv4OnlyVpnSetInterfaceFilteringRuleWithWildcard() throws Exception {
+    public void testLocalIpv4OnlyVpnInterfaceFilteringRule() throws Exception {
         LinkProperties lp = new LinkProperties();
         lp.setInterfaceName("tun0");
         lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun0"));
@@ -10470,26 +10475,31 @@
         mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
         assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
 
-        // IPv6 unreachable route should not be misinterpreted as a default route
-        // A connected VPN should have interface rules with null interface.
-        // Null Interface is a wildcard and this accepts traffic from all the interfaces.
-        // There are two expected invocations, one during the VPN initial connection,
-        // one during the VPN LinkProperties update.
-        ArgumentCaptor<int[]> uidCaptor = ArgumentCaptor.forClass(int[].class);
-        verify(mBpfNetMaps, times(2)).addUidInterfaceRules(
-                eq(null) /* iface */, uidCaptor.capture());
-        assertContainsExactly(uidCaptor.getAllValues().get(0), APP1_UID, APP2_UID, VPN_UID);
-        assertContainsExactly(uidCaptor.getAllValues().get(1), APP1_UID, APP2_UID, VPN_UID);
-        assertEquals(mService.mPermissionMonitor.getVpnInterfaceUidRanges(null /* iface */),
-                vpnRange);
+        if (SdkLevel.isAtLeastT()) {
+            // IPv6 unreachable route should not be misinterpreted as a default route
+            // On T and above, A connected VPN that does not provide a default route should have
+            // interface rules with null interface. Null Interface is a wildcard and this accepts
+            // traffic from all the interfaces. There are two expected invocations, one during the
+            // VPN initial connection, one during the VPN LinkProperties update.
+            ArgumentCaptor<int[]> uidCaptor = ArgumentCaptor.forClass(int[].class);
+            verify(mBpfNetMaps, times(2)).addUidInterfaceRules(
+                    eq(null) /* iface */, uidCaptor.capture());
+            assertContainsExactly(uidCaptor.getAllValues().get(0), APP1_UID, APP2_UID, VPN_UID);
+            assertContainsExactly(uidCaptor.getAllValues().get(1), APP1_UID, APP2_UID, VPN_UID);
+            assertEquals(mService.mPermissionMonitor.getVpnInterfaceUidRanges(null /* iface */),
+                    vpnRange);
 
-        mMockVpn.disconnect();
-        waitForIdle();
+            mMockVpn.disconnect();
+            waitForIdle();
 
-        // Disconnected VPN should have interface rules removed
-        verify(mBpfNetMaps).removeUidInterfaceRules(uidCaptor.capture());
-        assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID, VPN_UID);
-        assertNull(mService.mPermissionMonitor.getVpnInterfaceUidRanges(null /* iface */));
+            // Disconnected VPN should have interface rules removed
+            verify(mBpfNetMaps).removeUidInterfaceRules(uidCaptor.capture());
+            assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID, VPN_UID);
+            assertNull(mService.mPermissionMonitor.getVpnInterfaceUidRanges(null /* iface */));
+        } else {
+            // Before T, VPN with IPv6 unreachable route should not have interface rules.
+            verify(mBpfNetMaps, never()).addUidInterfaceRules(any(), any());
+        }
     }
 
     @Test
diff --git a/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
index 061dc87..9401d47 100644
--- a/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -89,7 +89,7 @@
 public class IpSecServiceParameterizedTest {
     @Rule
     public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(
-            Build.VERSION_CODES.R /* ignoreClassUpTo */);
+            Build.VERSION_CODES.S_V2 /* ignoreClassUpTo */);
 
     private static final int TEST_SPI = 0xD1201D;
 
diff --git a/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java b/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java
index 5c7ca6f..8595ab9 100644
--- a/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java
+++ b/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java
@@ -54,7 +54,7 @@
 /** Unit tests for {@link IpSecService.RefcountedResource}. */
 @SmallTest
 @RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 public class IpSecServiceRefcountedResourceTest {
     Context mMockContext;
     IpSecService.Dependencies mMockDeps;
diff --git a/tests/unit/java/com/android/server/IpSecServiceTest.java b/tests/unit/java/com/android/server/IpSecServiceTest.java
index 7e6b157..6955620 100644
--- a/tests/unit/java/com/android/server/IpSecServiceTest.java
+++ b/tests/unit/java/com/android/server/IpSecServiceTest.java
@@ -75,7 +75,7 @@
 /** Unit tests for {@link IpSecService}. */
 @SmallTest
 @RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 public class IpSecServiceTest {
 
     private static final int DROID_SPI = 0xD1201D;
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index 3c228d0..7ecf1f3 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
@@ -74,7 +75,7 @@
 //  - test NSD_ON ENABLE/DISABLED listening
 @RunWith(DevSdkIgnoreRunner.class)
 @SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 public class NsdServiceTest {
 
     static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD;
@@ -114,6 +115,10 @@
         doReturn(MDnsManager.MDNS_SERVICE).when(mContext)
                 .getSystemServiceName(MDnsManager.class);
         doReturn(mMockMDnsM).when(mContext).getSystemService(MDnsManager.MDNS_SERVICE);
+        if (mContext.getSystemService(MDnsManager.class) == null) {
+            // Test is using mockito-extended
+            doCallRealMethod().when(mContext).getSystemService(MDnsManager.class);
+        }
         doReturn(true).when(mMockMDnsM).registerService(
                 anyInt(), anyString(), anyString(), anyInt(), any(), anyInt());
         doReturn(true).when(mMockMDnsM).stopOperation(anyInt());
diff --git a/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
index 568d40f..4f849d2 100644
--- a/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
+++ b/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
@@ -16,8 +16,6 @@
 
 package com.android.server.ethernet;
 
-import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotSame;
@@ -55,16 +53,17 @@
 import android.net.StaticIpConfiguration;
 import android.net.ip.IpClientCallbacks;
 import android.net.ip.IpClientManager;
+import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.test.TestLooper;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.net.module.util.InterfaceParams;
 import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
 
 import org.junit.After;
 import org.junit.Before;
@@ -79,8 +78,9 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 
-@RunWith(AndroidJUnit4.class)
 @SmallTest
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 public class EthernetNetworkFactoryTest {
     private static final int TIMEOUT_MS = 2_000;
     private static final String TEST_IFACE = "test123";
@@ -608,7 +608,6 @@
         assertEquals(listener.expectOnResult(), TEST_IFACE);
     }
 
-    @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
     @Test
     public void testUpdateInterfaceAbortsOnConcurrentRemoveInterface() throws Exception {
         initEthernetNetworkFactory();
@@ -617,7 +616,6 @@
                 () -> mNetFactory.removeInterface(TEST_IFACE));
     }
 
-    @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
     @Test
     public void testUpdateInterfaceAbortsOnConcurrentUpdateInterfaceLinkState() throws Exception {
         initEthernetNetworkFactory();
@@ -626,7 +624,6 @@
                 () -> mNetFactory.updateInterfaceLinkState(TEST_IFACE, false, NULL_LISTENER));
     }
 
-    @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
     @Test
     public void testUpdateInterfaceCallsListenerCorrectlyOnConcurrentRequests() throws Exception {
         initEthernetNetworkFactory();
diff --git a/tests/unit/java/com/android/server/ethernet/EthernetServiceImplTest.java b/tests/unit/java/com/android/server/ethernet/EthernetServiceImplTest.java
index dd1f1ed..b2b9f2c 100644
--- a/tests/unit/java/com/android/server/ethernet/EthernetServiceImplTest.java
+++ b/tests/unit/java/com/android/server/ethernet/EthernetServiceImplTest.java
@@ -20,12 +20,12 @@
 
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.fail;
-
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
@@ -35,23 +35,25 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.net.INetworkInterfaceOutcomeReceiver;
 import android.net.EthernetNetworkUpdateRequest;
+import android.net.INetworkInterfaceOutcomeReceiver;
 import android.net.IpConfiguration;
 import android.net.NetworkCapabilities;
+import android.os.Build;
 import android.os.Handler;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 
-@RunWith(AndroidJUnit4.class)
 @SmallTest
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 public class EthernetServiceImplTest {
     private static final String TEST_IFACE = "test123";
     private static final EthernetNetworkUpdateRequest UPDATE_REQUEST =
@@ -69,14 +71,17 @@
                     .build();
     private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null;
     private EthernetServiceImpl mEthernetServiceImpl;
-    @Mock private Context mContext;
-    @Mock private Handler mHandler;
-    @Mock private EthernetTracker mEthernetTracker;
-    @Mock private PackageManager mPackageManager;
+    private Context mContext;
+    private Handler mHandler;
+    private EthernetTracker mEthernetTracker;
+    private PackageManager mPackageManager;
 
     @Before
     public void setup() {
-        MockitoAnnotations.initMocks(this);
+        mContext = mock(Context.class);
+        mHandler = mock(Handler.class);
+        mEthernetTracker = mock(EthernetTracker.class);
+        mPackageManager = mock(PackageManager.class);
         doReturn(mPackageManager).when(mContext).getPackageManager();
         mEthernetServiceImpl = new EthernetServiceImpl(mContext, mHandler, mEthernetTracker);
         mEthernetServiceImpl.mStarted.set(true);
@@ -111,18 +116,18 @@
     }
 
     @Test
-    public void testConnectNetworkRejectsWhenEthNotStarted() {
+    public void testEnableInterfaceRejectsWhenEthNotStarted() {
         mEthernetServiceImpl.mStarted.set(false);
         assertThrows(IllegalStateException.class, () -> {
-            mEthernetServiceImpl.connectNetwork("" /* iface */, null /* listener */);
+            mEthernetServiceImpl.enableInterface("" /* iface */, null /* listener */);
         });
     }
 
     @Test
-    public void testDisconnectNetworkRejectsWhenEthNotStarted() {
+    public void testDisableInterfaceRejectsWhenEthNotStarted() {
         mEthernetServiceImpl.mStarted.set(false);
         assertThrows(IllegalStateException.class, () -> {
-            mEthernetServiceImpl.disconnectNetwork("" /* iface */, null /* listener */);
+            mEthernetServiceImpl.disableInterface("" /* iface */, null /* listener */);
         });
     }
 
@@ -134,16 +139,16 @@
     }
 
     @Test
-    public void testConnectNetworkRejectsNullIface() {
+    public void testEnableInterfaceRejectsNullIface() {
         assertThrows(NullPointerException.class, () -> {
-            mEthernetServiceImpl.connectNetwork(null /* iface */, NULL_LISTENER);
+            mEthernetServiceImpl.enableInterface(null /* iface */, NULL_LISTENER);
         });
     }
 
     @Test
-    public void testDisconnectNetworkRejectsNullIface() {
+    public void testDisableInterfaceRejectsNullIface() {
         assertThrows(NullPointerException.class, () -> {
-            mEthernetServiceImpl.disconnectNetwork(null /* iface */, NULL_LISTENER);
+            mEthernetServiceImpl.disableInterface(null /* iface */, NULL_LISTENER);
         });
     }
 
@@ -165,22 +170,6 @@
                 eq(UPDATE_REQUEST_WITHOUT_CAPABILITIES.getNetworkCapabilities()), isNull());
     }
 
-    @Test
-    public void testConnectNetworkRejectsWithoutAutomotiveFeature() {
-        toggleAutomotiveFeature(false);
-        assertThrows(UnsupportedOperationException.class, () -> {
-            mEthernetServiceImpl.connectNetwork("" /* iface */, NULL_LISTENER);
-        });
-    }
-
-    @Test
-    public void testDisconnectNetworkRejectsWithoutAutomotiveFeature() {
-        toggleAutomotiveFeature(false);
-        assertThrows(UnsupportedOperationException.class, () -> {
-            mEthernetServiceImpl.disconnectNetwork("" /* iface */, NULL_LISTENER);
-        });
-    }
-
     private void denyManageEthPermission() {
         doThrow(new SecurityException("")).when(mContext)
                 .enforceCallingOrSelfPermission(
@@ -202,18 +191,18 @@
     }
 
     @Test
-    public void testConnectNetworkRejectsWithoutManageEthPermission() {
+    public void testEnableInterfaceRejectsWithoutManageEthPermission() {
         denyManageEthPermission();
         assertThrows(SecurityException.class, () -> {
-            mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER);
+            mEthernetServiceImpl.enableInterface(TEST_IFACE, NULL_LISTENER);
         });
     }
 
     @Test
-    public void testDisconnectNetworkRejectsWithoutManageEthPermission() {
+    public void testDisableInterfaceRejectsWithoutManageEthPermission() {
         denyManageEthPermission();
         assertThrows(SecurityException.class, () -> {
-            mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER);
+            mEthernetServiceImpl.disableInterface(TEST_IFACE, NULL_LISTENER);
         });
     }
 
@@ -231,20 +220,20 @@
     }
 
     @Test
-    public void testConnectNetworkRejectsTestRequestWithoutTestPermission() {
+    public void testEnableInterfaceRejectsTestRequestWithoutTestPermission() {
         enableTestInterface();
         denyManageTestNetworksPermission();
         assertThrows(SecurityException.class, () -> {
-            mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER);
+            mEthernetServiceImpl.enableInterface(TEST_IFACE, NULL_LISTENER);
         });
     }
 
     @Test
-    public void testDisconnectNetworkRejectsTestRequestWithoutTestPermission() {
+    public void testDisableInterfaceRejectsTestRequestWithoutTestPermission() {
         enableTestInterface();
         denyManageTestNetworksPermission();
         assertThrows(SecurityException.class, () -> {
-            mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER);
+            mEthernetServiceImpl.disableInterface(TEST_IFACE, NULL_LISTENER);
         });
     }
 
@@ -258,15 +247,15 @@
     }
 
     @Test
-    public void testConnectNetwork() {
-        mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER);
-        verify(mEthernetTracker).connectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER));
+    public void testEnableInterface() {
+        mEthernetServiceImpl.enableInterface(TEST_IFACE, NULL_LISTENER);
+        verify(mEthernetTracker).enableInterface(eq(TEST_IFACE), eq(NULL_LISTENER));
     }
 
     @Test
-    public void testDisconnectNetwork() {
-        mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER);
-        verify(mEthernetTracker).disconnectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER));
+    public void testDisableInterface() {
+        mEthernetServiceImpl.disableInterface(TEST_IFACE, NULL_LISTENER);
+        verify(mEthernetTracker).disableInterface(eq(TEST_IFACE), eq(NULL_LISTENER));
     }
 
     @Test
@@ -324,23 +313,23 @@
     }
 
     @Test
-    public void testConnectNetworkForTestRequestDoesNotRequireAutoOrNetPermission() {
+    public void testEnableInterfaceForTestRequestDoesNotRequireNetPermission() {
         enableTestInterface();
         toggleAutomotiveFeature(false);
         denyManageEthPermission();
 
-        mEthernetServiceImpl.connectNetwork(TEST_IFACE, NULL_LISTENER);
-        verify(mEthernetTracker).connectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER));
+        mEthernetServiceImpl.enableInterface(TEST_IFACE, NULL_LISTENER);
+        verify(mEthernetTracker).enableInterface(eq(TEST_IFACE), eq(NULL_LISTENER));
     }
 
     @Test
-    public void testDisconnectNetworkForTestRequestDoesNotRequireAutoOrNetPermission() {
+    public void testDisableInterfaceForTestRequestDoesNotRequireAutoOrNetPermission() {
         enableTestInterface();
         toggleAutomotiveFeature(false);
         denyManageEthPermission();
 
-        mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER);
-        verify(mEthernetTracker).disconnectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER));
+        mEthernetServiceImpl.disableInterface(TEST_IFACE, NULL_LISTENER);
+        verify(mEthernetTracker).disableInterface(eq(TEST_IFACE), eq(NULL_LISTENER));
     }
 
     private void denyPermissions(String... permissions) {
diff --git a/tests/unit/java/com/android/server/ethernet/EthernetTrackerTest.java b/tests/unit/java/com/android/server/ethernet/EthernetTrackerTest.java
index 33b36fd..4c35221 100644
--- a/tests/unit/java/com/android/server/ethernet/EthernetTrackerTest.java
+++ b/tests/unit/java/com/android/server/ethernet/EthernetTrackerTest.java
@@ -49,12 +49,14 @@
 import android.net.LinkAddress;
 import android.net.NetworkCapabilities;
 import android.net.StaticIpConfiguration;
+import android.os.Build;
 import android.os.HandlerThread;
 import android.os.RemoteException;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
 import com.android.testutils.HandlerUtils;
 
 import org.junit.After;
@@ -69,7 +71,8 @@
 import java.util.ArrayList;
 
 @SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 public class EthernetTrackerTest {
     private static final String TEST_IFACE = "test123";
     private static final int TIMEOUT_MS = 1_000;
@@ -352,8 +355,8 @@
     }
 
     @Test
-    public void testConnectNetworkCorrectlyCallsFactory() {
-        tracker.connectNetwork(TEST_IFACE, NULL_LISTENER);
+    public void testEnableInterfaceCorrectlyCallsFactory() {
+        tracker.enableInterface(TEST_IFACE, NULL_LISTENER);
         waitForIdle();
 
         verify(mFactory).updateInterfaceLinkState(eq(TEST_IFACE), eq(true /* up */),
@@ -361,8 +364,8 @@
     }
 
     @Test
-    public void testDisconnectNetworkCorrectlyCallsFactory() {
-        tracker.disconnectNetwork(TEST_IFACE, NULL_LISTENER);
+    public void testDisableInterfaceCorrectlyCallsFactory() {
+        tracker.disableInterface(TEST_IFACE, NULL_LISTENER);
         waitForIdle();
 
         verify(mFactory).updateInterfaceLinkState(eq(TEST_IFACE), eq(false /* up */),
diff --git a/tests/unit/java/com/android/server/net/BpfInterfaceMapUpdaterTest.java b/tests/unit/java/com/android/server/net/BpfInterfaceMapUpdaterTest.java
index 987b7b7..c6852d1 100644
--- a/tests/unit/java/com/android/server/net/BpfInterfaceMapUpdaterTest.java
+++ b/tests/unit/java/com/android/server/net/BpfInterfaceMapUpdaterTest.java
@@ -24,16 +24,18 @@
 import android.content.Context;
 import android.net.INetd;
 import android.net.MacAddress;
+import android.os.Build;
 import android.os.Handler;
 import android.os.test.TestLooper;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
 import com.android.net.module.util.IBpfMap;
 import com.android.net.module.util.InterfaceParams;
 import com.android.net.module.util.Struct.U32;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -42,8 +44,9 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RunWith(AndroidJUnit4.class)
 @SmallTest
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 public final class BpfInterfaceMapUpdaterTest {
     private static final int TEST_INDEX = 1;
     private static final int TEST_INDEX2 = 2;
diff --git a/tests/unit/java/com/android/server/net/IpConfigStoreTest.java b/tests/unit/java/com/android/server/net/IpConfigStoreTest.java
index e9a5309..1801c45 100644
--- a/tests/unit/java/com/android/server/net/IpConfigStoreTest.java
+++ b/tests/unit/java/com/android/server/net/IpConfigStoreTest.java
@@ -27,9 +27,11 @@
 import android.net.LinkAddress;
 import android.net.ProxyInfo;
 import android.net.StaticIpConfiguration;
+import android.os.Build;
 import android.util.ArrayMap;
 
-import androidx.test.runner.AndroidJUnit4;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -46,7 +48,8 @@
 /**
  * Unit tests for {@link IpConfigStore}
  */
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 public class IpConfigStoreTest {
     private static final int KEY_CONFIG = 17;
     private static final String IFACE_1 = "eth0";
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
index 79744b1..5400a00 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -29,6 +29,7 @@
 import static android.net.NetworkStats.UID_ALL;
 
 import static com.android.server.net.NetworkStatsFactory.kernelToTag;
+import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
@@ -38,7 +39,6 @@
 import android.net.NetworkStats;
 import android.net.TrafficStats;
 import android.net.UnderlyingNetworkInfo;
-import android.os.Build;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -67,7 +67,7 @@
 /** Tests for {@link NetworkStatsFactory}. */
 @RunWith(DevSdkIgnoreRunner.class)
 @SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(SC_V2)
 public class NetworkStatsFactoryTest extends NetworkStatsBaseTest {
     private static final String CLAT_PREFIX = "v4-";
 
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
index e8c9637..5747e10 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
@@ -274,8 +274,12 @@
 
         mStatsObservers.unregister(request, UID_BLUE);
         waitForObserverToIdle();
-
         Mockito.verifyZeroInteractions(mUsageCallbackBinder);
+
+        // Verify that system uid can unregister for other uids.
+        mStatsObservers.unregister(request, Process.SYSTEM_UID);
+        waitForObserverToIdle();
+        mUsageCallback.expectOnCallbackReleased(request);
     }
 
     private NetworkIdentitySet makeTestIdentSet() {
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
index 0d34609..622f2be 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
@@ -19,6 +19,8 @@
 import static android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
 import static android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA;
 
+import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
@@ -37,7 +39,6 @@
 import android.annotation.Nullable;
 import android.app.usage.NetworkStatsManager;
 import android.content.Context;
-import android.os.Build;
 import android.os.Looper;
 import android.os.Parcel;
 import android.telephony.SubscriptionManager;
@@ -63,7 +64,7 @@
 import java.util.concurrent.Executors;
 
 @RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(SC_V2)
 public final class NetworkStatsSubscriptionsMonitorTest {
     private static final int TEST_SUBID1 = 3;
     private static final int TEST_SUBID2 = 5;