Merge "Rename libbpf to libbpf_bcc"
diff --git a/OWNERS b/OWNERS
index 62c5737..07a775e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,2 +1,4 @@
 set noparent
 file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
+
+per-file **IpSec* = file:platform/frameworks/base:master:/services/core/java/com/android/server/vcn/OWNERS
\ No newline at end of file
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index 3c49383..0b54783 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -56,6 +56,7 @@
     ],
     plugins: ["java_api_finder"],
     manifest: "AndroidManifestBase.xml",
+    lint: { strict_updatability_linting: true },
 }
 
 // build tethering static library, used to compile both variants of the tethering.
@@ -69,6 +70,7 @@
         "NetworkStackApiCurrentShims",
     ],
     apex_available: ["com.android.tethering"],
+    lint: { strict_updatability_linting: true },
 }
 
 android_library {
@@ -81,6 +83,7 @@
         "NetworkStackApiStableShims",
     ],
     apex_available: ["com.android.tethering"],
+    lint: { strict_updatability_linting: true },
 }
 
 // Due to b/143733063, APK can't access a jni lib that is in APEX (but not in the APK).
@@ -94,7 +97,7 @@
     min_sdk_version: "30",
     header_libs: [
         "bpf_syscall_wrappers",
-        "bpf_tethering_headers",
+        "bpf_connectivity_headers",
     ],
     srcs: [
         "jni/*.cpp",
@@ -147,6 +150,7 @@
     optimize: {
         proguard_flags_files: ["proguard.flags"],
     },
+    lint: { strict_updatability_linting: true },
 }
 
 // Non-updatable tethering running in the system server process for devices not using the module
@@ -159,6 +163,7 @@
     // InProcessTethering is a replacement for Tethering
     overrides: ["Tethering"],
     apex_available: ["com.android.tethering"],
+    lint: { strict_updatability_linting: true },
 }
 
 // Updatable tethering packaged for finalized API
@@ -175,11 +180,16 @@
         "privapp_whitelist_com.android.networkstack.tethering",
     ],
     apex_available: ["com.android.tethering"],
+    lint: { strict_updatability_linting: true },
 }
 
 android_app {
     name: "TetheringNext",
-    defaults: ["TetheringAppDefaults", "TetheringApiLevel"],
+    defaults: [
+        "TetheringAppDefaults",
+        "TetheringApiLevel",
+        "ConnectivityNextEnableDefaults",
+    ],
     static_libs: ["TetheringApiCurrentLib"],
     certificate: "networkstack",
     manifest: "AndroidManifest.xml",
@@ -190,6 +200,7 @@
         "privapp_whitelist_com.android.networkstack.tethering",
     ],
     apex_available: ["com.android.tethering"],
+    lint: { strict_updatability_linting: true },
 }
 
 sdk {
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index a5216f7..7863572 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -18,8 +18,26 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
+// Defaults to enable/disable java targets which uses development APIs. "enabled" may have a
+// different value depending on the branch.
+java_defaults {
+    name: "ConnectivityNextEnableDefaults",
+    enabled: true,
+}
+apex_defaults {
+    name: "ConnectivityApexDefaults",
+    // Tethering app to include in the AOSP apex. Branches that disable the "next" targets may use
+    // a stable tethering app instead, but will generally override the AOSP apex to use updatable
+    // package names and keys, so that apex will be unused anyway.
+    apps: ["TetheringNext"], // Replace to "Tethering" if ConnectivityNextEnableDefaults is false.
+}
+// This is a placeholder comment to avoid merge conflicts
+// as the above target may have different "enabled" values
+// depending on the branch
+
 apex {
     name: "com.android.tethering",
+    defaults: ["ConnectivityApexDefaults"],
     compile_multilib: "both",
     updatable: true,
     min_sdk_version: "30",
@@ -43,7 +61,6 @@
     ],
     apps: [
         "ServiceConnectivityResources",
-        "TetheringNext",
     ],
     prebuilts: ["current_sdkinfo"],
     manifest: "manifest.json",
diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp
index 6e64570..c82a993 100644
--- a/Tethering/common/TetheringLib/Android.bp
+++ b/Tethering/common/TetheringLib/Android.bp
@@ -53,6 +53,7 @@
     apex_available: ["com.android.tethering"],
     permitted_packages: ["android.net"],
     min_sdk_version: "30",
+    lint: { strict_updatability_linting: true },
 }
 
 filegroup {
diff --git a/Tethering/jni/com_android_networkstack_tethering_BpfUtils.cpp b/Tethering/jni/com_android_networkstack_tethering_BpfUtils.cpp
index 9838bf1..f9e4824 100644
--- a/Tethering/jni/com_android_networkstack_tethering_BpfUtils.cpp
+++ b/Tethering/jni/com_android_networkstack_tethering_BpfUtils.cpp
@@ -26,6 +26,7 @@
 #include <net/if.h>
 #include <stdio.h>
 #include <sys/socket.h>
+#include <sys/utsname.h>
 
 // TODO: use unique_fd.
 #define BPF_FD_JUST_USE_INT
@@ -158,6 +159,37 @@
     return rv;
 }
 
+// -----------------------------------------------------------------------------
+// TODO - just use BpfUtils.h once that is available in sc-mainline-prod and has kernelVersion()
+//
+// In the mean time copying verbatim from:
+//   system/bpf/libbpf_android/include/bpf/BpfUtils.h
+// and
+//   system/bpf/libbpf_android/BpfUtils.cpp
+
+#define KVER(a, b, c) (((a) << 24) + ((b) << 16) + (c))
+
+static unsigned kernelVersion() {
+    struct utsname buf;
+    int ret = uname(&buf);
+    if (ret) return 0;
+
+    unsigned kver_major;
+    unsigned kver_minor;
+    unsigned kver_sub;
+    char discard;
+    ret = sscanf(buf.release, "%u.%u.%u%c", &kver_major, &kver_minor, &kver_sub, &discard);
+    // Check the device kernel version
+    if (ret < 3) return 0;
+
+    return KVER(kver_major, kver_minor, kver_sub);
+}
+
+static inline bool isAtLeastKernelVersion(unsigned major, unsigned minor, unsigned sub) {
+    return kernelVersion() >= KVER(major, minor, sub);
+}
+// -----------------------------------------------------------------------------
+
 static jboolean com_android_networkstack_tethering_BpfUtils_isEthernet(JNIEnv* env, jobject clazz,
                                                                        jstring iface) {
     ScopedUtfChars interface(env, iface);
@@ -170,13 +202,30 @@
         return false;
     }
 
+    // Backwards compatibility with pre-GKI kernels that use various custom
+    // ARPHRD_* for their cellular interface
+    switch (rv) {
+        // ARPHRD_PUREIP on at least some Mediatek Android kernels
+        // example: wembley with 4.19 kernel
+        case 520:
+        // in Linux 4.14+ rmnet support was upstreamed and ARHRD_RAWIP became 519,
+        // but it is 530 on at least some Qualcomm Android 4.9 kernels with rmnet
+        // example: Pixel 3 family
+        case 530:
+            // >5.4 kernels are GKI2.0 and thus upstream compatible, however 5.10
+            // shipped with Android S, so (for safety) let's limit ourselves to
+            // >5.10, ie. 5.11+ as a guarantee we're on Android T+ and thus no
+            // longer need this non-upstream compatibility logic
+            static bool is_pre_5_11_kernel = !isAtLeastKernelVersion(5, 11, 0);
+            if (is_pre_5_11_kernel) return false;
+    }
+
     switch (rv) {
         case ARPHRD_ETHER:
             return true;
         case ARPHRD_NONE:
         case ARPHRD_PPP:
-        case ARPHRD_RAWIP:  // in Linux 4.14+ rmnet support was upstreamed and this is 519
-        case 530:           // this is ARPHRD_RAWIP on some Android 4.9 kernels with rmnet
+        case ARPHRD_RAWIP:
             return false;
         default:
             jniThrowExceptionFmt(env, "java/io/IOException",
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 78f2afc..55c24d3 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -123,6 +123,7 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Pair;
 import android.util.SparseArray;
 
 import androidx.annotation.NonNull;
@@ -267,6 +268,9 @@
     private String mConfiguredEthernetIface;
     private EthernetCallback mEthernetCallback;
     private SettingsObserver mSettingsObserver;
+    private BluetoothPan mBluetoothPan;
+    private PanServiceListener mBluetoothPanListener;
+    private ArrayList<Pair<Boolean, IIntResultListener>> mPendingPanRequests;
 
     public Tethering(TetheringDependencies deps) {
         mLog.mark("Tethering.constructed");
@@ -276,6 +280,11 @@
         mLooper = mDeps.getTetheringLooper();
         mNotificationUpdater = mDeps.getNotificationUpdater(mContext, mLooper);
 
+        // This is intended to ensrure that if something calls startTethering(bluetooth) just after
+        // bluetooth is enabled. Before onServiceConnected is called, store the calls into this
+        // list and handle them as soon as onServiceConnected is called.
+        mPendingPanRequests = new ArrayList<>();
+
         mTetherStates = new ArrayMap<>();
         mConnectedClientsTracker = new ConnectedClientsTracker();
 
@@ -701,35 +710,82 @@
             return;
         }
 
-        adapter.getProfileProxy(mContext, new ServiceListener() {
-            @Override
-            public void onServiceDisconnected(int profile) { }
+        if (mBluetoothPanListener != null && mBluetoothPanListener.isConnected()) {
+            // The PAN service is connected. Enable or disable bluetooth tethering.
+            // When bluetooth tethering is enabled, any time a PAN client pairs with this
+            // host, bluetooth will bring up a bt-pan interface and notify tethering to
+            // enable IP serving.
+            setBluetoothTetheringSettings(mBluetoothPan, enable, listener);
+            return;
+        }
 
-            @Override
-            public void onServiceConnected(int profile, BluetoothProfile proxy) {
-                // Clear identify is fine because caller already pass tethering permission at
-                // ConnectivityService#startTethering()(or stopTethering) before the control comes
-                // here. Bluetooth will check tethering permission again that there is
-                // Context#getOpPackageName() under BluetoothPan#setBluetoothTethering() to get
-                // caller's package name for permission check.
-                // Calling BluetoothPan#setBluetoothTethering() here means the package name always
-                // be system server. If calling identity is not cleared, that package's uid might
-                // not match calling uid and end up in permission denied.
-                final long identityToken = Binder.clearCallingIdentity();
-                try {
-                    ((BluetoothPan) proxy).setBluetoothTethering(enable);
-                } finally {
-                    Binder.restoreCallingIdentity(identityToken);
+        // The reference of IIntResultListener should only exist when application want to start
+        // tethering but tethering is not bound to pan service yet. Even if the calling process
+        // dies, the referenice of IIntResultListener would still keep in mPendingPanRequests. Once
+        // tethering bound to pan service (onServiceConnected) or bluetooth just crash
+        // (onServiceDisconnected), all the references from mPendingPanRequests would be cleared.
+        mPendingPanRequests.add(new Pair(enable, listener));
+
+        // Bluetooth tethering is not a popular feature. To avoid bind to bluetooth pan service all
+        // the time but user never use bluetooth tethering. mBluetoothPanListener is created first
+        // time someone calls a bluetooth tethering method (even if it's just to disable tethering
+        // when it's already disabled) and never unset after that.
+        if (mBluetoothPanListener == null) {
+            mBluetoothPanListener = new PanServiceListener();
+            adapter.getProfileProxy(mContext, mBluetoothPanListener, BluetoothProfile.PAN);
+        }
+    }
+
+    private class PanServiceListener implements ServiceListener {
+        private boolean mIsConnected = false;
+
+        @Override
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            // Posting this to handling onServiceConnected in tethering handler thread may have
+            // race condition that bluetooth service may disconnected when tethering thread
+            // actaully handle onServiceconnected. If this race happen, calling
+            // BluetoothPan#setBluetoothTethering would silently fail. It is fine because pan
+            // service is unreachable and both bluetooth and bluetooth tethering settings are off.
+            mHandler.post(() -> {
+                mBluetoothPan = (BluetoothPan) proxy;
+                mIsConnected = true;
+
+                for (Pair<Boolean, IIntResultListener> request : mPendingPanRequests) {
+                    setBluetoothTetheringSettings(mBluetoothPan, request.first, request.second);
                 }
-                // TODO: Enabling bluetooth tethering can fail asynchronously here.
-                // We should figure out a way to bubble up that failure instead of sending success.
-                final int result = (((BluetoothPan) proxy).isTetheringOn() == enable)
-                        ? TETHER_ERROR_NO_ERROR
-                        : TETHER_ERROR_INTERNAL_ERROR;
-                sendTetherResult(listener, result, TETHERING_BLUETOOTH);
-                adapter.closeProfileProxy(BluetoothProfile.PAN, proxy);
-            }
-        }, BluetoothProfile.PAN);
+                mPendingPanRequests.clear();
+            });
+        }
+
+        @Override
+        public void onServiceDisconnected(int profile) {
+            mHandler.post(() -> {
+                // onServiceDisconnected means Bluetooth is off (or crashed) and is not
+                // reachable before next onServiceConnected.
+                mIsConnected = false;
+
+                for (Pair<Boolean, IIntResultListener> request : mPendingPanRequests) {
+                    sendTetherResult(request.second, TETHER_ERROR_SERVICE_UNAVAIL,
+                            TETHERING_BLUETOOTH);
+                }
+                mPendingPanRequests.clear();
+            });
+        }
+
+        public boolean isConnected() {
+            return mIsConnected;
+        }
+    }
+
+    private void setBluetoothTetheringSettings(@NonNull final BluetoothPan bluetoothPan,
+            final boolean enable, final IIntResultListener listener) {
+        bluetoothPan.setBluetoothTethering(enable);
+
+        // Enabling bluetooth tethering settings can silently fail. Send internal error if the
+        // result is not expected.
+        final int result = bluetoothPan.isTetheringOn() == enable
+                ? TETHER_ERROR_NO_ERROR : TETHER_ERROR_INTERNAL_ERROR;
+        sendTetherResult(listener, result, TETHERING_BLUETOOTH);
     }
 
     private int setEthernetTethering(final boolean enable) {
diff --git a/Tethering/tests/privileged/Android.bp b/Tethering/tests/privileged/Android.bp
index 214b014..c890197 100644
--- a/Tethering/tests/privileged/Android.bp
+++ b/Tethering/tests/privileged/Android.bp
@@ -34,6 +34,7 @@
     name: "TetheringPrivilegedTests",
     defaults: [
         "TetheringPrivilegedTestsJniDefaults",
+        "ConnectivityNextEnableDefaults",
     ],
     srcs: [
         "src/**/*.java",
diff --git a/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java b/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
index 646c75f..ad2faa0 100644
--- a/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
+++ b/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
@@ -50,7 +50,7 @@
 @RunWith(DevSdkIgnoreRunner.class)
 @IgnoreUpTo(Build.VERSION_CODES.R)
 public final class BpfMapTest {
-    // Sync from packages/modules/Connectivity/Tethering/bpf_progs/offload.c.
+    // Sync from packages/modules/Connectivity/bpf_progs/offload.c.
     private static final int TEST_MAP_SIZE = 16;
     private static final String TETHER_DOWNSTREAM6_FS_PATH =
             "/sys/fs/bpf/tethering/map_test_tether_downstream6_map";
diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp
index 228f3fd..5150d39 100644
--- a/Tethering/tests/unit/Android.bp
+++ b/Tethering/tests/unit/Android.bp
@@ -49,7 +49,6 @@
         "src/**/*.kt",
     ],
     static_libs: [
-        "TetheringApiCurrentLib",
         "TetheringCommonTests",
         "androidx.test.rules",
         "frameworks-base-testutils",
@@ -85,6 +84,9 @@
 android_library {
     name: "TetheringTestsLatestSdkLib",
     defaults: ["TetheringTestsDefaults"],
+    static_libs: [
+        "TetheringApiStableLib",
+    ],
     target_sdk_version: "30",
     visibility: [
         "//packages/modules/Connectivity/tests:__subpackages__",
@@ -99,7 +101,13 @@
         "device-tests",
         "mts-tethering",
     ],
-    defaults: ["TetheringTestsDefaults"],
+    defaults: [
+        "TetheringTestsDefaults",
+        "ConnectivityNextEnableDefaults",
+    ],
+    static_libs: [
+        "TetheringApiCurrentLib",
+    ],
     compile_multilib: "both",
     jarjar_rules: ":TetheringTestsJarJarRules",
 }
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index f45768f..40d133a 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -2558,10 +2558,10 @@
     @Test
     public void testBluetoothTethering() throws Exception {
         final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR);
-        when(mBluetoothAdapter.isEnabled()).thenReturn(true);
+        mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
         mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), result);
         mLooper.dispatchAll();
-        verifySetBluetoothTethering(true);
+        verifySetBluetoothTethering(true /* enable */, true /* bindToPanService */);
         result.assertHasResult();
 
         mTethering.interfaceAdded(TEST_BT_IFNAME);
@@ -2574,6 +2574,64 @@
         mLooper.dispatchAll();
         tetherResult.assertHasResult();
 
+        verifyNetdCommandForBtSetup();
+
+        // Turning tethering on a second time does not bind to the PAN service again, since it's
+        // already bound.
+        mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
+        final ResultListener secondResult = new ResultListener(TETHER_ERROR_NO_ERROR);
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), secondResult);
+        mLooper.dispatchAll();
+        verifySetBluetoothTethering(true /* enable */, false /* bindToPanService */);
+        secondResult.assertHasResult();
+
+        mockBluetoothSettings(true /* bluetoothOn */, false /* tetheringOn */);
+        mTethering.stopTethering(TETHERING_BLUETOOTH);
+        mLooper.dispatchAll();
+        final ResultListener untetherResult = new ResultListener(TETHER_ERROR_NO_ERROR);
+        mTethering.untether(TEST_BT_IFNAME, untetherResult);
+        mLooper.dispatchAll();
+        untetherResult.assertHasResult();
+        verifySetBluetoothTethering(false /* enable */, false /* bindToPanService */);
+
+        verifyNetdCommandForBtTearDown();
+    }
+
+    @Test
+    public void testBluetoothServiceDisconnects() throws Exception {
+        final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR);
+        mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), result);
+        mLooper.dispatchAll();
+        ServiceListener panListener = verifySetBluetoothTethering(true /* enable */,
+                true /* bindToPanService */);
+        result.assertHasResult();
+
+        mTethering.interfaceAdded(TEST_BT_IFNAME);
+        mLooper.dispatchAll();
+
+        mTethering.interfaceStatusChanged(TEST_BT_IFNAME, false);
+        mTethering.interfaceStatusChanged(TEST_BT_IFNAME, true);
+        final ResultListener tetherResult = new ResultListener(TETHER_ERROR_NO_ERROR);
+        mTethering.tether(TEST_BT_IFNAME, IpServer.STATE_TETHERED, tetherResult);
+        mLooper.dispatchAll();
+        tetherResult.assertHasResult();
+
+        verifyNetdCommandForBtSetup();
+
+        panListener.onServiceDisconnected(BluetoothProfile.PAN);
+        mTethering.interfaceStatusChanged(TEST_BT_IFNAME, false);
+        mLooper.dispatchAll();
+
+        verifyNetdCommandForBtTearDown();
+    }
+
+    private void mockBluetoothSettings(boolean bluetoothOn, boolean tetheringOn) {
+        when(mBluetoothAdapter.isEnabled()).thenReturn(bluetoothOn);
+        when(mBluetoothPan.isTetheringOn()).thenReturn(tetheringOn);
+    }
+
+    private void verifyNetdCommandForBtSetup() throws Exception {
         verify(mNetd).tetherInterfaceAdd(TEST_BT_IFNAME);
         verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, TEST_BT_IFNAME);
         verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_BT_IFNAME),
@@ -2584,39 +2642,41 @@
                 anyString(), anyString());
         verifyNoMoreInteractions(mNetd);
         reset(mNetd);
+    }
 
-        when(mBluetoothAdapter.isEnabled()).thenReturn(true);
-        mTethering.stopTethering(TETHERING_BLUETOOTH);
-        mLooper.dispatchAll();
-        final ResultListener untetherResult = new ResultListener(TETHER_ERROR_NO_ERROR);
-        mTethering.untether(TEST_BT_IFNAME, untetherResult);
-        mLooper.dispatchAll();
-        untetherResult.assertHasResult();
-        verifySetBluetoothTethering(false);
-
+    private void verifyNetdCommandForBtTearDown() throws Exception {
         verify(mNetd).tetherApplyDnsInterfaces();
         verify(mNetd).tetherInterfaceRemove(TEST_BT_IFNAME);
         verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_BT_IFNAME);
         verify(mNetd).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
         verify(mNetd).tetherStop();
         verify(mNetd).ipfwdDisableForwarding(TETHERING_NAME);
-        verifyNoMoreInteractions(mNetd);
     }
 
-    private void verifySetBluetoothTethering(final boolean enable) {
-        final ArgumentCaptor<ServiceListener> listenerCaptor =
-                ArgumentCaptor.forClass(ServiceListener.class);
+    // If bindToPanService is true, this function would return ServiceListener which could notify
+    // PanService is connected or disconnected.
+    private ServiceListener verifySetBluetoothTethering(final boolean enable,
+            final boolean bindToPanService) {
+        ServiceListener listener = null;
         verify(mBluetoothAdapter).isEnabled();
-        verify(mBluetoothAdapter).getProfileProxy(eq(mServiceContext), listenerCaptor.capture(),
-                eq(BluetoothProfile.PAN));
-        final ServiceListener listener = listenerCaptor.getValue();
-        when(mBluetoothPan.isTetheringOn()).thenReturn(enable);
-        listener.onServiceConnected(BluetoothProfile.PAN, mBluetoothPan);
+        if (bindToPanService) {
+            final ArgumentCaptor<ServiceListener> listenerCaptor =
+                    ArgumentCaptor.forClass(ServiceListener.class);
+            verify(mBluetoothAdapter).getProfileProxy(eq(mServiceContext), listenerCaptor.capture(),
+                    eq(BluetoothProfile.PAN));
+            listener = listenerCaptor.getValue();
+            listener.onServiceConnected(BluetoothProfile.PAN, mBluetoothPan);
+            mLooper.dispatchAll();
+        } else {
+            verify(mBluetoothAdapter, never()).getProfileProxy(eq(mServiceContext), any(),
+                    anyInt());
+        }
         verify(mBluetoothPan).setBluetoothTethering(enable);
         verify(mBluetoothPan).isTetheringOn();
-        verify(mBluetoothAdapter).closeProfileProxy(eq(BluetoothProfile.PAN), eq(mBluetoothPan));
         verifyNoMoreInteractions(mBluetoothAdapter, mBluetoothPan);
         reset(mBluetoothAdapter, mBluetoothPan);
+
+        return listener;
     }
 
     private void runDualStackUsbTethering(final String expectedIface) throws Exception {
diff --git a/Tethering/bpf_progs/Android.bp b/bpf_progs/Android.bp
similarity index 69%
rename from Tethering/bpf_progs/Android.bp
rename to bpf_progs/Android.bp
index 5b00dfe..17eebe0 100644
--- a/Tethering/bpf_progs/Android.bp
+++ b/bpf_progs/Android.bp
@@ -22,7 +22,7 @@
 }
 
 cc_library_headers {
-    name: "bpf_tethering_headers",
+    name: "bpf_connectivity_headers",
     vendor_available: false,
     host_supported: false,
     export_include_dirs: ["."],
@@ -32,9 +32,20 @@
     ],
     sdk_version: "30",
     min_sdk_version: "30",
-    apex_available: ["com.android.tethering"],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.tethering",
+        ],
     visibility: [
+        // TODO: remove it when NetworkStatsService is moved into the mainline module and no more
+        // calls to JNI in libservices.core.
+        "//frameworks/base/services/core/jni",
         "//packages/modules/Connectivity/Tethering",
+        "//packages/modules/Connectivity/tests/unit/jni",
+        // TODO: remove system/netd/* when all BPF code is moved out of Netd.
+        "//system/netd/libnetdbpf",
+        "//system/netd/server",
+        "//system/netd/tests",
     ],
 }
 
diff --git a/Tethering/bpf_progs/bpf_net_helpers.h b/bpf_progs/bpf_net_helpers.h
similarity index 100%
rename from Tethering/bpf_progs/bpf_net_helpers.h
rename to bpf_progs/bpf_net_helpers.h
diff --git a/bpf_progs/bpf_shared.h b/bpf_progs/bpf_shared.h
new file mode 100644
index 0000000..8577d9d
--- /dev/null
+++ b/bpf_progs/bpf_shared.h
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2018 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 <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <netdutils/UidConstants.h>
+
+// This header file is shared by eBPF kernel programs (C) and netd (C++) and
+// some of the maps are also accessed directly from Java mainline module code.
+//
+// Hence: explicitly pad all relevant structures and assert that their size
+// is the sum of the sizes of their fields.
+#define STRUCT_SIZE(name, size) _Static_assert(sizeof(name) == (size), "Incorrect struct size.")
+
+typedef struct {
+    uint32_t uid;
+    uint32_t tag;
+} UidTagValue;
+STRUCT_SIZE(UidTagValue, 2 * 4);  // 8
+
+typedef struct {
+    uint32_t uid;
+    uint32_t tag;
+    uint32_t counterSet;
+    uint32_t ifaceIndex;
+} StatsKey;
+STRUCT_SIZE(StatsKey, 4 * 4);  // 16
+
+typedef struct {
+    uint64_t rxPackets;
+    uint64_t rxBytes;
+    uint64_t txPackets;
+    uint64_t txBytes;
+} StatsValue;
+STRUCT_SIZE(StatsValue, 4 * 8);  // 32
+
+typedef struct {
+    char name[IFNAMSIZ];
+} IfaceValue;
+STRUCT_SIZE(IfaceValue, 16);
+
+typedef struct {
+    uint64_t rxBytes;
+    uint64_t rxPackets;
+    uint64_t txBytes;
+    uint64_t txPackets;
+    uint64_t tcpRxPackets;
+    uint64_t tcpTxPackets;
+} Stats;
+
+// Since we cannot garbage collect the stats map since device boot, we need to make these maps as
+// large as possible. The maximum size of number of map entries we can have is depend on the rlimit
+// of MEM_LOCK granted to netd. The memory space needed by each map can be calculated by the
+// following fomula:
+//      elem_size = 40 + roundup(key_size, 8) + roundup(value_size, 8)
+//      cost = roundup_pow_of_two(max_entries) * 16 + elem_size * max_entries +
+//              elem_size * number_of_CPU
+// And the cost of each map currently used is(assume the device have 8 CPUs):
+// cookie_tag_map:      key:  8 bytes, value:  8 bytes, cost:  822592 bytes    =   823Kbytes
+// uid_counter_set_map: key:  4 bytes, value:  1 bytes, cost:  145216 bytes    =   145Kbytes
+// app_uid_stats_map:   key:  4 bytes, value: 32 bytes, cost: 1062784 bytes    =  1063Kbytes
+// uid_stats_map:       key: 16 bytes, value: 32 bytes, cost: 1142848 bytes    =  1143Kbytes
+// tag_stats_map:       key: 16 bytes, value: 32 bytes, cost: 1142848 bytes    =  1143Kbytes
+// iface_index_name_map:key:  4 bytes, value: 16 bytes, cost:   80896 bytes    =    81Kbytes
+// iface_stats_map:     key:  4 bytes, value: 32 bytes, cost:   97024 bytes    =    97Kbytes
+// dozable_uid_map:     key:  4 bytes, value:  1 bytes, cost:  145216 bytes    =   145Kbytes
+// standby_uid_map:     key:  4 bytes, value:  1 bytes, cost:  145216 bytes    =   145Kbytes
+// powersave_uid_map:   key:  4 bytes, value:  1 bytes, cost:  145216 bytes    =   145Kbytes
+// total:                                                                         4930Kbytes
+// It takes maximum 4.9MB kernel memory space if all maps are full, which requires any devices
+// running this module to have a memlock rlimit to be larger then 5MB. In the old qtaguid module,
+// we don't have a total limit for data entries but only have limitation of tags each uid can have.
+// (default is 1024 in kernel);
+
+// 'static' - otherwise these constants end up in .rodata in the resulting .o post compilation
+static const int COOKIE_UID_MAP_SIZE = 10000;
+static const int UID_COUNTERSET_MAP_SIZE = 2000;
+static const int APP_STATS_MAP_SIZE = 10000;
+static const int STATS_MAP_SIZE = 5000;
+static const int IFACE_INDEX_NAME_MAP_SIZE = 1000;
+static const int IFACE_STATS_MAP_SIZE = 1000;
+static const int CONFIGURATION_MAP_SIZE = 2;
+static const int UID_OWNER_MAP_SIZE = 2000;
+
+#define BPF_PATH "/sys/fs/bpf/"
+
+#define BPF_EGRESS_PROG_PATH BPF_PATH "prog_netd_cgroupskb_egress_stats"
+#define BPF_INGRESS_PROG_PATH BPF_PATH "prog_netd_cgroupskb_ingress_stats"
+#define XT_BPF_INGRESS_PROG_PATH BPF_PATH "prog_netd_skfilter_ingress_xtbpf"
+#define XT_BPF_EGRESS_PROG_PATH BPF_PATH "prog_netd_skfilter_egress_xtbpf"
+#define XT_BPF_ALLOWLIST_PROG_PATH BPF_PATH "prog_netd_skfilter_allowlist_xtbpf"
+#define XT_BPF_DENYLIST_PROG_PATH BPF_PATH "prog_netd_skfilter_denylist_xtbpf"
+#define CGROUP_SOCKET_PROG_PATH BPF_PATH "prog_netd_cgroupsock_inet_create"
+
+#define TC_BPF_INGRESS_ACCOUNT_PROG_NAME "prog_netd_schedact_ingress_account"
+#define TC_BPF_INGRESS_ACCOUNT_PROG_PATH BPF_PATH TC_BPF_INGRESS_ACCOUNT_PROG_NAME
+
+#define COOKIE_TAG_MAP_PATH BPF_PATH "map_netd_cookie_tag_map"
+#define UID_COUNTERSET_MAP_PATH BPF_PATH "map_netd_uid_counterset_map"
+#define APP_UID_STATS_MAP_PATH BPF_PATH "map_netd_app_uid_stats_map"
+#define STATS_MAP_A_PATH BPF_PATH "map_netd_stats_map_A"
+#define STATS_MAP_B_PATH BPF_PATH "map_netd_stats_map_B"
+#define IFACE_INDEX_NAME_MAP_PATH BPF_PATH "map_netd_iface_index_name_map"
+#define IFACE_STATS_MAP_PATH BPF_PATH "map_netd_iface_stats_map"
+#define CONFIGURATION_MAP_PATH BPF_PATH "map_netd_configuration_map"
+#define UID_OWNER_MAP_PATH BPF_PATH "map_netd_uid_owner_map"
+#define UID_PERMISSION_MAP_PATH BPF_PATH "map_netd_uid_permission_map"
+
+enum UidOwnerMatchType {
+    NO_MATCH = 0,
+    HAPPY_BOX_MATCH = (1 << 0),
+    PENALTY_BOX_MATCH = (1 << 1),
+    DOZABLE_MATCH = (1 << 2),
+    STANDBY_MATCH = (1 << 3),
+    POWERSAVE_MATCH = (1 << 4),
+    RESTRICTED_MATCH = (1 << 5),
+    IIF_MATCH = (1 << 6),
+};
+
+enum BpfPermissionMatch {
+    BPF_PERMISSION_INTERNET = 1 << 2,
+    BPF_PERMISSION_UPDATE_DEVICE_STATS = 1 << 3,
+};
+// In production we use two identical stats maps to record per uid stats and
+// do swap and clean based on the configuration specified here. The statsMapType
+// value in configuration map specified which map is currently in use.
+enum StatsMapType {
+    SELECT_MAP_A,
+    SELECT_MAP_B,
+};
+
+// TODO: change the configuration object from an 8-bit bitmask to an object with clearer
+// semantics, like a struct.
+typedef uint8_t BpfConfig;
+static const BpfConfig DEFAULT_CONFIG = 0;
+
+typedef struct {
+    // Allowed interface index. Only applicable if IIF_MATCH is set in the rule bitmask above.
+    uint32_t iif;
+    // A bitmask of enum values in UidOwnerMatchType.
+    uint32_t rule;
+} UidOwnerValue;
+STRUCT_SIZE(UidOwnerValue, 2 * 4);  // 8
+
+#define UID_RULES_CONFIGURATION_KEY 1
+#define CURRENT_STATS_MAP_CONFIGURATION_KEY 2
+
+#define CLAT_INGRESS6_PROG_RAWIP_NAME "prog_clatd_schedcls_ingress6_clat_rawip"
+#define CLAT_INGRESS6_PROG_ETHER_NAME "prog_clatd_schedcls_ingress6_clat_ether"
+
+#define CLAT_INGRESS6_PROG_RAWIP_PATH BPF_PATH CLAT_INGRESS6_PROG_RAWIP_NAME
+#define CLAT_INGRESS6_PROG_ETHER_PATH BPF_PATH CLAT_INGRESS6_PROG_ETHER_NAME
+
+#define CLAT_INGRESS6_MAP_PATH BPF_PATH "map_clatd_clat_ingress6_map"
+
+typedef struct {
+    uint32_t iif;            // The input interface index
+    struct in6_addr pfx96;   // The source /96 nat64 prefix, bottom 32 bits must be 0
+    struct in6_addr local6;  // The full 128-bits of the destination IPv6 address
+} ClatIngress6Key;
+STRUCT_SIZE(ClatIngress6Key, 4 + 2 * 16);  // 36
+
+typedef struct {
+    uint32_t oif;           // The output interface to redirect to (0 means don't redirect)
+    struct in_addr local4;  // The destination IPv4 address
+} ClatIngress6Value;
+STRUCT_SIZE(ClatIngress6Value, 4 + 4);  // 8
+
+#define CLAT_EGRESS4_PROG_RAWIP_NAME "prog_clatd_schedcls_egress4_clat_rawip"
+#define CLAT_EGRESS4_PROG_ETHER_NAME "prog_clatd_schedcls_egress4_clat_ether"
+
+#define CLAT_EGRESS4_PROG_RAWIP_PATH BPF_PATH CLAT_EGRESS4_PROG_RAWIP_NAME
+#define CLAT_EGRESS4_PROG_ETHER_PATH BPF_PATH CLAT_EGRESS4_PROG_ETHER_NAME
+
+#define CLAT_EGRESS4_MAP_PATH BPF_PATH "map_clatd_clat_egress4_map"
+
+typedef struct {
+    uint32_t iif;           // The input interface index
+    struct in_addr local4;  // The source IPv4 address
+} ClatEgress4Key;
+STRUCT_SIZE(ClatEgress4Key, 4 + 4);  // 8
+
+typedef struct {
+    uint32_t oif;            // The output interface to redirect to
+    struct in6_addr local6;  // The full 128-bits of the source IPv6 address
+    struct in6_addr pfx96;   // The destination /96 nat64 prefix, bottom 32 bits must be 0
+    bool oifIsEthernet;      // Whether the output interface requires ethernet header
+    uint8_t pad[3];
+} ClatEgress4Value;
+STRUCT_SIZE(ClatEgress4Value, 4 + 2 * 16 + 1 + 3);  // 40
+
+#undef STRUCT_SIZE
diff --git a/Tethering/bpf_progs/bpf_tethering.h b/bpf_progs/bpf_tethering.h
similarity index 98%
rename from Tethering/bpf_progs/bpf_tethering.h
rename to bpf_progs/bpf_tethering.h
index 5fdf8cd..b0ec8f6 100644
--- a/Tethering/bpf_progs/bpf_tethering.h
+++ b/bpf_progs/bpf_tethering.h
@@ -24,7 +24,7 @@
 // Common definitions for BPF code in the tethering mainline module.
 // These definitions are available to:
 // - The BPF programs in Tethering/bpf_progs/
-// - JNI code that depends on the bpf_tethering_headers library.
+// - JNI code that depends on the bpf_connectivity_headers library.
 
 #define BPF_TETHER_ERRORS    \
     ERR(INVALID_IP_VERSION)  \
diff --git a/Tethering/bpf_progs/offload.c b/bpf_progs/offload.c
similarity index 100%
rename from Tethering/bpf_progs/offload.c
rename to bpf_progs/offload.c
diff --git a/Tethering/bpf_progs/test.c b/bpf_progs/test.c
similarity index 100%
rename from Tethering/bpf_progs/test.c
rename to bpf_progs/test.c
diff --git a/framework/Android.bp b/framework/Android.bp
index d31f74f..e765ee8 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -112,6 +112,7 @@
     apex_available: [
         "com.android.tethering",
     ],
+    lint: { strict_updatability_linting: true },
 }
 
 cc_library_shared {
diff --git a/framework/aidl-export/android/net/DhcpOption.aidl b/framework/aidl-export/android/net/DhcpOption.aidl
new file mode 100644
index 0000000..9ed0e62
--- /dev/null
+++ b/framework/aidl-export/android/net/DhcpOption.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2021, 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;
+
+parcelable DhcpOption;
+
diff --git a/framework/api/current.txt b/framework/api/current.txt
index 33f4d14..9a77a3c 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -196,6 +196,7 @@
   }
 
   public static class DnsResolver.DnsException extends java.lang.Exception {
+    ctor public DnsResolver.DnsException(int, @Nullable Throwable);
     field public final int code;
   }
 
diff --git a/framework/api/module-lib-current.txt b/framework/api/module-lib-current.txt
index 7fc0382..50dd2ad 100644
--- a/framework/api/module-lib-current.txt
+++ b/framework/api/module-lib-current.txt
@@ -99,6 +99,15 @@
     field public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = 3; // 0x3
   }
 
+  public final class DhcpOption implements android.os.Parcelable {
+    ctor public DhcpOption(byte, @Nullable byte[]);
+    method public int describeContents();
+    method public byte getType();
+    method @Nullable public byte[] getValue();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.DhcpOption> CREATOR;
+  }
+
   public final class NetworkAgentConfig implements android.os.Parcelable {
     method @Nullable public String getSubscriberId();
     method public boolean isBypassableVpn();
diff --git a/framework/api/module-lib-lint-baseline.txt b/framework/api/module-lib-lint-baseline.txt
new file mode 100644
index 0000000..c7b0db5
--- /dev/null
+++ b/framework/api/module-lib-lint-baseline.txt
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+NoByteOrShort: android.net.DhcpOption#DhcpOption(byte, byte[]) parameter #0:
+    Should avoid odd sized primitives; use `int` instead of `byte` in parameter type in android.net.DhcpOption(byte type, byte[] value)
+NoByteOrShort: android.net.DhcpOption#describeContents():
+    Should avoid odd sized primitives; use `int` instead of `byte` in method android.net.DhcpOption.describeContents()
+NoByteOrShort: android.net.DhcpOption#getType():
+    Should avoid odd sized primitives; use `int` instead of `byte` in method android.net.DhcpOption.getType()
diff --git a/framework/lint-baseline.xml b/framework/lint-baseline.xml
deleted file mode 100644
index 099202f..0000000
--- a/framework/lint-baseline.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `new android.net.ParseException`"
-        errorLine1="                ParseException pe = new ParseException(e.reason, e.getCause());"
-        errorLine2="                                    ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Connectivity/framework/src/android/net/DnsResolver.java"
-            line="301"
-            column="37"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback`"
-        errorLine1="    protected class ActiveDataSubscriptionIdListener extends TelephonyCallback"
-        errorLine2="                                                             ~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java"
-            line="96"
-            column="62"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener`"
-        errorLine1="            implements TelephonyCallback.ActiveDataSubscriptionIdListener {"
-        errorLine2="                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java"
-            line="97"
-            column="24"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#registerTelephonyCallback`"
-        errorLine1="        ctx.getSystemService(TelephonyManager.class).registerTelephonyCallback("
-        errorLine2="                                                     ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java"
-            line="126"
-            column="54"/>
-    </issue>
-
-</issues>
diff --git a/framework/src/android/net/DhcpOption.java b/framework/src/android/net/DhcpOption.java
new file mode 100644
index 0000000..a125290
--- /dev/null
+++ b/framework/src/android/net/DhcpOption.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class representing an option in the DHCP protocol.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class DhcpOption implements Parcelable {
+    private final byte mType;
+    private final byte[] mValue;
+
+    /**
+     * Constructs a DhcpOption object.
+     *
+     * @param type the type of this option
+     * @param value the value of this option. If {@code null}, DHCP packets containing this option
+     *              will include the option type in the Parameter Request List. Otherwise, DHCP
+     *              packets containing this option will include the option in the options section.
+     */
+    public DhcpOption(byte type, @Nullable byte[] value) {
+        mType = type;
+        mValue = value;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeByte(mType);
+        dest.writeByteArray(mValue);
+    }
+
+    /** Implement the Parcelable interface */
+    public static final @NonNull Creator<DhcpOption> CREATOR =
+            new Creator<DhcpOption>() {
+                public DhcpOption createFromParcel(Parcel in) {
+                    return new DhcpOption(in.readByte(), in.createByteArray());
+                }
+
+                public DhcpOption[] newArray(int size) {
+                    return new DhcpOption[size];
+                }
+            };
+
+    /** Get the type of DHCP option */
+    public byte getType() {
+        return mType;
+    }
+
+    /** Get the value of DHCP option */
+    @Nullable public byte[] getValue() {
+        return mValue == null ? null : mValue.clone();
+    }
+}
diff --git a/framework/src/android/net/DnsResolver.java b/framework/src/android/net/DnsResolver.java
index dac88ad..164160f 100644
--- a/framework/src/android/net/DnsResolver.java
+++ b/framework/src/android/net/DnsResolver.java
@@ -164,7 +164,7 @@
         */
         @DnsError public final int code;
 
-        DnsException(@DnsError int code, @Nullable Throwable cause) {
+        public DnsException(@DnsError int code, @Nullable Throwable cause) {
             super(cause);
             this.code = code;
         }
diff --git a/framework/src/android/net/Network.java b/framework/src/android/net/Network.java
index b3770ea..53f171a 100644
--- a/framework/src/android/net/Network.java
+++ b/framework/src/android/net/Network.java
@@ -382,13 +382,14 @@
         // Query a property of the underlying socket to ensure that the socket's file descriptor
         // exists, is available to bind to a network and is not closed.
         socket.getReuseAddress();
-        final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket);
-        bindSocket(pfd.getFileDescriptor());
-        // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the
-        // dup share the underlying socket in the kernel. The socket is never truly closed until the
-        // last fd pointing to the socket being closed. So close the dup one after binding the
-        // socket to control the lifetime of the dup fd.
-        pfd.close();
+
+        // ParcelFileDescriptor.fromDatagramSocket() creates a dup of the original fd. The original
+        // and the dup share the underlying socket in the kernel. The socket is never truly closed
+        // until the last fd pointing to the socket being closed. Try and eventually close the dup
+        // one after binding the socket to control the lifetime of the dup fd.
+        try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket)) {
+            bindSocket(pfd.getFileDescriptor());
+        }
     }
 
     /**
@@ -400,13 +401,13 @@
         // Query a property of the underlying socket to ensure that the socket's file descriptor
         // exists, is available to bind to a network and is not closed.
         socket.getReuseAddress();
-        final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
-        bindSocket(pfd.getFileDescriptor());
-        // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the
-        // dup share the underlying socket in the kernel. The socket is never truly closed until the
-        // last fd pointing to the socket being closed. So close the dup one after binding the
-        // socket to control the lifetime of the dup fd.
-        pfd.close();
+        // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and
+        // the dup share the underlying socket in the kernel. The socket is never truly closed
+        // until the last fd pointing to the socket being closed. Try and eventually close the dup
+        // one after binding the socket to control the lifetime of the dup fd.
+        try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket)) {
+            bindSocket(pfd.getFileDescriptor());
+        }
     }
 
     /**
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index 75f0129..03cf109 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -787,18 +787,6 @@
         }
     }
 
-    private void combineNetCapabilities(@NonNull NetworkCapabilities nc) {
-        final long wantedCaps = this.mNetworkCapabilities | nc.mNetworkCapabilities;
-        final long forbiddenCaps =
-                this.mForbiddenNetworkCapabilities | nc.mForbiddenNetworkCapabilities;
-        if ((wantedCaps & forbiddenCaps) != 0) {
-            throw new IllegalArgumentException(
-                    "Cannot have the same capability in wanted and forbidden lists.");
-        }
-        this.mNetworkCapabilities = wantedCaps;
-        this.mForbiddenNetworkCapabilities = forbiddenCaps;
-    }
-
     /**
      * Convenience function that returns a human-readable description of the first mutable
      * capability we find. Used to present an error message to apps that request mutable
@@ -1109,10 +1097,6 @@
         return mTransportTypes == (1 << transportType);
     }
 
-    private void combineTransportTypes(NetworkCapabilities nc) {
-        this.mTransportTypes |= nc.mTransportTypes;
-    }
-
     private boolean satisfiedByTransportTypes(NetworkCapabilities nc) {
         return ((this.mTransportTypes == 0)
                 || ((this.mTransportTypes & nc.mTransportTypes) != 0));
@@ -1293,26 +1277,6 @@
     }
 
     /**
-     * Combine the administrator UIDs of the capabilities.
-     *
-     * <p>This is only legal if either of the administrators lists are empty, or if they are equal.
-     * Combining administrator UIDs is only possible for combining non-overlapping sets of UIDs.
-     *
-     * <p>If both administrator lists are non-empty but not equal, they conflict with each other. In
-     * this case, it would not make sense to add them together.
-     */
-    private void combineAdministratorUids(@NonNull final NetworkCapabilities nc) {
-        if (nc.mAdministratorUids.length == 0) return;
-        if (mAdministratorUids.length == 0) {
-            mAdministratorUids = Arrays.copyOf(nc.mAdministratorUids, nc.mAdministratorUids.length);
-            return;
-        }
-        if (!equalsAdministratorUids(nc)) {
-            throw new IllegalStateException("Can't combine two different administrator UID lists");
-        }
-    }
-
-    /**
      * Value indicating that link bandwidth is unspecified.
      * @hide
      */
@@ -1374,12 +1338,6 @@
         return mLinkDownBandwidthKbps;
     }
 
-    private void combineLinkBandwidths(NetworkCapabilities nc) {
-        this.mLinkUpBandwidthKbps =
-                Math.max(this.mLinkUpBandwidthKbps, nc.mLinkUpBandwidthKbps);
-        this.mLinkDownBandwidthKbps =
-                Math.max(this.mLinkDownBandwidthKbps, nc.mLinkDownBandwidthKbps);
-    }
     private boolean satisfiedByLinkBandwidths(NetworkCapabilities nc) {
         return !(this.mLinkUpBandwidthKbps > nc.mLinkUpBandwidthKbps
                 || this.mLinkDownBandwidthKbps > nc.mLinkDownBandwidthKbps);
@@ -1466,13 +1424,6 @@
         return mTransportInfo;
     }
 
-    private void combineSpecifiers(NetworkCapabilities nc) {
-        if (mNetworkSpecifier != null && !mNetworkSpecifier.equals(nc.mNetworkSpecifier)) {
-            throw new IllegalStateException("Can't combine two networkSpecifiers");
-        }
-        setNetworkSpecifier(nc.mNetworkSpecifier);
-    }
-
     private boolean satisfiedBySpecifier(NetworkCapabilities nc) {
         return mNetworkSpecifier == null || mNetworkSpecifier.canBeSatisfiedBy(nc.mNetworkSpecifier)
                 || nc.mNetworkSpecifier instanceof MatchAllNetworkSpecifier;
@@ -1482,13 +1433,6 @@
         return Objects.equals(mNetworkSpecifier, nc.mNetworkSpecifier);
     }
 
-    private void combineTransportInfos(NetworkCapabilities nc) {
-        if (mTransportInfo != null && !mTransportInfo.equals(nc.mTransportInfo)) {
-            throw new IllegalStateException("Can't combine two TransportInfos");
-        }
-        setTransportInfo(nc.mTransportInfo);
-    }
-
     private boolean equalsTransportInfo(NetworkCapabilities nc) {
         return Objects.equals(mTransportInfo, nc.mTransportInfo);
     }
@@ -1543,10 +1487,6 @@
         return mSignalStrength;
     }
 
-    private void combineSignalStrength(NetworkCapabilities nc) {
-        this.mSignalStrength = Math.max(this.mSignalStrength, nc.mSignalStrength);
-    }
-
     private boolean satisfiedBySignalStrength(NetworkCapabilities nc) {
         return this.mSignalStrength <= nc.mSignalStrength;
     }
@@ -1652,28 +1592,6 @@
     }
 
     /**
-     * Compare if the given NetworkCapabilities have the same UIDs.
-     *
-     * @hide
-     */
-    public static boolean hasSameUids(@Nullable NetworkCapabilities nc1,
-            @Nullable NetworkCapabilities nc2) {
-        final Set<UidRange> uids1 = (nc1 == null) ? null : nc1.mUids;
-        final Set<UidRange> uids2 = (nc2 == null) ? null : nc2.mUids;
-        if (null == uids1) return null == uids2;
-        if (null == uids2) return false;
-        // Make a copy so it can be mutated to check that all ranges in uids2 also are in uids.
-        final Set<UidRange> uids = new ArraySet<>(uids2);
-        for (UidRange range : uids1) {
-            if (!uids.contains(range)) {
-                return false;
-            }
-            uids.remove(range);
-        }
-        return uids.isEmpty();
-    }
-
-    /**
      * Tests if the set of UIDs that this network applies to is the same as the passed network.
      * <p>
      * This test only checks whether equal range objects are in both sets. It will
@@ -1683,13 +1601,13 @@
      * Note that this method is not very optimized, which is fine as long as it's not used very
      * often.
      * <p>
-     * nc is assumed nonnull.
+     * nc is assumed nonnull, else NPE.
      *
      * @hide
      */
     @VisibleForTesting
     public boolean equalsUids(@NonNull NetworkCapabilities nc) {
-        return hasSameUids(nc, this);
+        return UidRange.hasSameUids(nc.mUids, mUids);
     }
 
     /**
@@ -1729,7 +1647,7 @@
      * @hide
      */
     @VisibleForTesting
-    public boolean appliesToUidRange(@Nullable UidRange requiredRange) {
+    public boolean appliesToUidRange(@NonNull UidRange requiredRange) {
         if (null == mUids) return true;
         for (UidRange uidRange : mUids) {
             if (uidRange.containsRange(requiredRange)) {
@@ -1740,20 +1658,6 @@
     }
 
     /**
-     * Combine the UIDs this network currently applies to with the UIDs the passed
-     * NetworkCapabilities apply to.
-     * nc is assumed nonnull.
-     */
-    private void combineUids(@NonNull NetworkCapabilities nc) {
-        if (null == nc.mUids || null == mUids) {
-            mUids = null;
-            return;
-        }
-        mUids.addAll(nc.mUids);
-    }
-
-
-    /**
      * The SSID of the network, or null if not applicable or unknown.
      * <p>
      * This is filled in by wifi code.
@@ -1796,42 +1700,6 @@
     }
 
     /**
-     * Combine SSIDs of the capabilities.
-     * <p>
-     * This is only legal if either the SSID of this object is null, or both SSIDs are
-     * equal.
-     * @hide
-     */
-    private void combineSSIDs(@NonNull NetworkCapabilities nc) {
-        if (mSSID != null && !mSSID.equals(nc.mSSID)) {
-            throw new IllegalStateException("Can't combine two SSIDs");
-        }
-        setSSID(nc.mSSID);
-    }
-
-    /**
-     * Combine a set of Capabilities to this one.  Useful for coming up with the complete set.
-     * <p>
-     * Note that this method may break an invariant of having a particular capability in either
-     * wanted or forbidden lists but never in both.  Requests that have the same capability in
-     * both lists will never be satisfied.
-     * @hide
-     */
-    public void combineCapabilities(@NonNull NetworkCapabilities nc) {
-        combineNetCapabilities(nc);
-        combineTransportTypes(nc);
-        combineLinkBandwidths(nc);
-        combineSpecifiers(nc);
-        combineTransportInfos(nc);
-        combineSignalStrength(nc);
-        combineUids(nc);
-        combineSSIDs(nc);
-        combineRequestor(nc);
-        combineAdministratorUids(nc);
-        combineSubscriptionIds(nc);
-    }
-
-    /**
      * Check if our requirements are satisfied by the given {@code NetworkCapabilities}.
      *
      * @param nc the {@code NetworkCapabilities} that may or may not satisfy our requirements.
@@ -2406,25 +2274,6 @@
         return TextUtils.equals(mRequestorPackageName, nc.mRequestorPackageName);
     }
 
-    /**
-     * Combine requestor info of the capabilities.
-     * <p>
-     * This is only legal if either the requestor info of this object is reset, or both info are
-     * equal.
-     * nc is assumed nonnull.
-     */
-    private void combineRequestor(@NonNull NetworkCapabilities nc) {
-        if (mRequestorUid != Process.INVALID_UID && mRequestorUid != nc.mOwnerUid) {
-            throw new IllegalStateException("Can't combine two uids");
-        }
-        if (mRequestorPackageName != null
-                && !mRequestorPackageName.equals(nc.mRequestorPackageName)) {
-            throw new IllegalStateException("Can't combine two package names");
-        }
-        setRequestorUid(nc.mRequestorUid);
-        setRequestorPackageName(nc.mRequestorPackageName);
-    }
-
     private boolean equalsRequestor(NetworkCapabilities nc) {
         return mRequestorUid == nc.mRequestorUid
                 && TextUtils.equals(mRequestorPackageName, nc.mRequestorPackageName);
@@ -2484,20 +2333,6 @@
     }
 
     /**
-     * Combine subscription ID set of the capabilities.
-     *
-     * <p>This is only legal if the subscription Ids are equal.
-     *
-     * <p>If both subscription IDs are not equal, they belong to different subscription
-     * (or no subscription). In this case, it would not make sense to add them together.
-     */
-    private void combineSubscriptionIds(@NonNull NetworkCapabilities nc) {
-        if (!Objects.equals(mSubIds, nc.mSubIds)) {
-            throw new IllegalStateException("Can't combine two subscription ID sets");
-        }
-    }
-
-    /**
      * Returns a bitmask of all the applicable redactions (based on the permissions held by the
      * receiving app) to be performed on this object.
      *
diff --git a/framework/src/android/net/UidRange.java b/framework/src/android/net/UidRange.java
index bd33292..a1f64f2 100644
--- a/framework/src/android/net/UidRange.java
+++ b/framework/src/android/net/UidRange.java
@@ -180,4 +180,24 @@
         }
         return uids;
     }
+
+    /**
+     * Compare if the given UID range sets have the same UIDs.
+     *
+     * @hide
+     */
+    public static boolean hasSameUids(@Nullable Set<UidRange> uids1,
+            @Nullable Set<UidRange> uids2) {
+        if (null == uids1) return null == uids2;
+        if (null == uids2) return false;
+        // Make a copy so it can be mutated to check that all ranges in uids2 also are in uids.
+        final Set<UidRange> remainingUids = new ArraySet<>(uids2);
+        for (UidRange range : uids1) {
+            if (!remainingUids.contains(range)) {
+                return false;
+            }
+            remainingUids.remove(range);
+        }
+        return remainingUids.isEmpty();
+    }
 }
diff --git a/framework/src/android/net/util/MultinetworkPolicyTracker.java b/framework/src/android/net/util/MultinetworkPolicyTracker.java
index 3e7cb80..c1790c9 100644
--- a/framework/src/android/net/util/MultinetworkPolicyTracker.java
+++ b/framework/src/android/net/util/MultinetworkPolicyTracker.java
@@ -20,6 +20,7 @@
 import static android.net.ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE;
 
 import android.annotation.NonNull;
+import android.annotation.TargetApi;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -29,6 +30,7 @@
 import android.database.ContentObserver;
 import android.net.ConnectivityResources;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Handler;
 import android.provider.Settings;
 import android.telephony.SubscriptionManager;
@@ -92,8 +94,8 @@
             }
         }
     }
-
-    @VisibleForTesting
+    // TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is addressed.
+    @VisibleForTesting @TargetApi(Build.VERSION_CODES.S)
     protected class ActiveDataSubscriptionIdListener extends TelephonyCallback
             implements TelephonyCallback.ActiveDataSubscriptionIdListener {
         @Override
@@ -107,6 +109,8 @@
         this(ctx, handler, null);
     }
 
+    // TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is addressed.
+    @TargetApi(Build.VERSION_CODES.S)
     public MultinetworkPolicyTracker(Context ctx, Handler handler, Runnable avoidBadWifiCallback) {
         mContext = ctx;
         mResources = new ConnectivityResources(ctx);
diff --git a/service/Android.bp b/service/Android.bp
index 02717f7..b595ef2 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -79,6 +79,7 @@
     apex_available: [
         "com.android.tethering",
     ],
+    lint: { strict_updatability_linting: true },
 }
 
 java_library {
@@ -95,6 +96,7 @@
     apex_available: [
         "com.android.tethering",
     ],
+    lint: { strict_updatability_linting: true },
 }
 
 java_library {
@@ -109,6 +111,7 @@
     apex_available: [
         "com.android.tethering",
     ],
+    lint: { strict_updatability_linting: true },
 }
 
 filegroup {
diff --git a/service/lint-baseline.xml b/service/lint-baseline.xml
deleted file mode 100644
index 119b64f..0000000
--- a/service/lint-baseline.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#isDataCapable`"
-        errorLine1="            if (tm.isDataCapable()) {"
-        errorLine2="                   ~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java"
-            line="787"
-            column="20"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.content.Context#sendStickyBroadcast`"
-        errorLine1="                mUserAllContext.sendStickyBroadcast(intent, options);"
-        errorLine2="                                ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java"
-            line="2681"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.content.pm.PackageManager#getTargetSdkVersion`"
-        errorLine1="            final int callingVersion = pm.getTargetSdkVersion(callingPackageName);"
-        errorLine2="                                          ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java"
-            line="5851"
-            column="43"/>
-    </issue>
-
-</issues>
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 222f5c8..6227bb2 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -98,6 +98,7 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TargetApi;
 import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
 import android.app.PendingIntent;
@@ -853,6 +854,9 @@
             mTypeLists = new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1];
         }
 
+        // TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is
+        //  addressed.
+        @TargetApi(Build.VERSION_CODES.S)
         public void loadSupportedTypes(@NonNull Context ctx, @NonNull TelephonyManager tm) {
             final PackageManager pm = ctx.getPackageManager();
             if (pm.hasSystemFeature(FEATURE_WIFI)) {
@@ -2356,6 +2360,26 @@
         return false;
     }
 
+    private int getAppUid(final String app, final UserHandle user) {
+        final PackageManager pm =
+                mContext.createContextAsUser(user, 0 /* flags */).getPackageManager();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return pm.getPackageUid(app, 0 /* flags */);
+        } catch (PackageManager.NameNotFoundException e) {
+            return -1;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    private void verifyCallingUidAndPackage(String packageName, int callingUid) {
+        final UserHandle user = UserHandle.getUserHandleForUid(callingUid);
+        if (getAppUid(packageName, user) != callingUid) {
+            throw new SecurityException(packageName + " does not belong to uid " + callingUid);
+        }
+    }
+
     /**
      * Ensure that a network route exists to deliver traffic to the specified
      * host via the specified network interface.
@@ -2371,6 +2395,7 @@
         if (disallowedBecauseSystemCaller()) {
             return false;
         }
+        verifyCallingUidAndPackage(callingPackageName, mDeps.getCallingUid());
         enforceChangePermission(callingPackageName, callingAttributionTag);
         if (mProtectedNetworks.contains(networkType)) {
             enforceConnectivityRestrictedNetworksPermission();
@@ -2760,6 +2785,8 @@
         sendStickyBroadcast(makeGeneralIntent(info, bcastType));
     }
 
+    // TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is addressed.
+    @TargetApi(Build.VERSION_CODES.S)
     private void sendStickyBroadcast(Intent intent) {
         synchronized (this) {
             if (!mSystemReady
@@ -6109,6 +6136,8 @@
         }
     }
 
+    // TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is addressed.
+    @TargetApi(Build.VERSION_CODES.S)
     private boolean isTargetSdkAtleast(int version, int callingUid,
             @NonNull String callingPackageName) {
         final UserHandle user = UserHandle.getUserHandleForUid(callingUid);
@@ -7676,7 +7705,9 @@
         // changed.
         // TODO: Try to track the default network that apps use and only send a proxy broadcast when
         //  that happens to prevent false alarms.
-        if (nai.isVPN() && nai.everConnected && !NetworkCapabilities.hasSameUids(prevNc, newNc)
+        final Set<UidRange> prevUids = prevNc == null ? null : prevNc.getUidRanges();
+        final Set<UidRange> newUids = newNc == null ? null : newNc.getUidRanges();
+        if (nai.isVPN() && nai.everConnected && !UidRange.hasSameUids(prevUids, newUids)
                 && (nai.linkProperties.getHttpProxy() != null || isProxySetOnAnyDefaultNetwork())) {
             mProxyTracker.sendProxyBroadcast();
         }
diff --git a/service/src/com/android/server/ConnectivityServiceInitializer.java b/service/src/com/android/server/ConnectivityServiceInitializer.java
index 2465479..b1a56ae 100644
--- a/service/src/com/android/server/ConnectivityServiceInitializer.java
+++ b/service/src/com/android/server/ConnectivityServiceInitializer.java
@@ -31,7 +31,6 @@
         super(context);
         // Load JNI libraries used by ConnectivityService and its dependencies
         System.loadLibrary("service-connectivity");
-        // TODO: Define formal APIs to get the needed services.
         mConnectivity = new ConnectivityService(context);
     }
 
diff --git a/service/src/com/android/server/connectivity/NetworkRanker.java b/service/src/com/android/server/connectivity/NetworkRanker.java
index d7eb9c8..43da1d0 100644
--- a/service/src/com/android/server/connectivity/NetworkRanker.java
+++ b/service/src/com/android/server/connectivity/NetworkRanker.java
@@ -63,8 +63,6 @@
         NetworkCapabilities getCapsNoCopy();
     }
 
-    private static final boolean USE_POLICY_RANKING = true;
-
     public NetworkRanker() { }
 
     /**
@@ -77,11 +75,7 @@
         final ArrayList<NetworkAgentInfo> candidates = filter(nais, nai -> nai.satisfies(request));
         if (candidates.size() == 1) return candidates.get(0); // Only one potential satisfier
         if (candidates.size() <= 0) return null; // No network can satisfy this request
-        if (USE_POLICY_RANKING) {
-            return getBestNetworkByPolicy(candidates, currentSatisfier);
-        } else {
-            return getBestNetworkByLegacyInt(candidates);
-        }
+        return getBestNetworkByPolicy(candidates, currentSatisfier);
     }
 
     // Transport preference order, if it comes down to that.
@@ -278,23 +272,6 @@
         return candidates.get(0);
     }
 
-    // TODO : switch to the policy implementation and remove
-    // Almost equivalent to Collections.max(nais), but allows returning null if no network
-    // satisfies the request.
-    private NetworkAgentInfo getBestNetworkByLegacyInt(
-            @NonNull final Collection<NetworkAgentInfo> nais) {
-        NetworkAgentInfo bestNetwork = null;
-        int bestScore = Integer.MIN_VALUE;
-        for (final NetworkAgentInfo nai : nais) {
-            final int naiScore = nai.getCurrentScore();
-            if (naiScore > bestScore) {
-                bestNetwork = nai;
-                bestScore = naiScore;
-            }
-        }
-        return bestNetwork;
-    }
-
     /**
      * Returns whether a {@link Scoreable} has a chance to beat a champion network for a request.
      *
@@ -322,30 +299,11 @@
         // If there is no satisfying network, then this network can beat, because some network
         // is always better than no network.
         if (null == champion) return true;
-        if (USE_POLICY_RANKING) {
-            // If there is no champion, the offer can always beat.
-            // Otherwise rank them.
-            final ArrayList<Scoreable> candidates = new ArrayList<>();
-            candidates.add(champion);
-            candidates.add(contestant);
-            return contestant == getBestNetworkByPolicy(candidates, champion);
-        } else {
-            return mightBeatByLegacyInt(champion.getScore(), contestant);
-        }
-    }
-
-    /**
-     * Returns whether a contestant might beat a champion according to the legacy int.
-     */
-    private boolean mightBeatByLegacyInt(@Nullable final FullScore championScore,
-            @NonNull final Scoreable contestant) {
-        final int offerIntScore;
-        if (contestant.getCapsNoCopy().hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
-            // If the offer might have Internet access, then it might validate.
-            offerIntScore = contestant.getScore().getLegacyIntAsValidated();
-        } else {
-            offerIntScore = contestant.getScore().getLegacyInt();
-        }
-        return championScore.getLegacyInt() < offerIntScore;
+        // If there is no champion, the offer can always beat.
+        // Otherwise rank them.
+        final ArrayList<Scoreable> candidates = new ArrayList<>();
+        candidates.add(champion);
+        candidates.add(contestant);
+        return contestant == getBestNetworkByPolicy(candidates, champion);
     }
 }
diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java
index da2715e..439db89 100755
--- a/service/src/com/android/server/connectivity/PermissionMonitor.java
+++ b/service/src/com/android/server/connectivity/PermissionMonitor.java
@@ -23,7 +23,6 @@
 import static android.Manifest.permission.UPDATE_DEVICE_STATS;
 import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
-import static android.content.pm.PackageManager.MATCH_ANY_USER;
 import static android.net.ConnectivitySettingsManager.UIDS_ALLOWED_ON_RESTRICTED_NETWORKS;
 import static android.net.INetd.PERMISSION_INTERNET;
 import static android.net.INetd.PERMISSION_NETWORK;
@@ -60,9 +59,9 @@
 import android.os.UserManager;
 import android.provider.Settings;
 import android.system.OsConstants;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
-import android.util.SparseArray;
 import android.util.SparseIntArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -98,9 +97,9 @@
     @GuardedBy("this")
     private final Set<UserHandle> mUsers = new HashSet<>();
 
-    // Keys are appIds. Values are true for SYSTEM permission and false for NETWORK permission.
+    // Keys are uids. Values are netd network permissions.
     @GuardedBy("this")
-    private final SparseIntArray mApps = new SparseIntArray();
+    private final SparseIntArray mUidToNetworkPerm = new SparseIntArray();
 
     // Keys are active non-bypassable and fully-routed VPN's interface name, Values are uid ranges
     // for apps under the VPN
@@ -121,6 +120,11 @@
     @GuardedBy("this")
     private final Set<Integer> mUidsAllowedOnRestrictedNetworks = new ArraySet<>();
 
+    @GuardedBy("this")
+    private final Map<UserHandle, PackageManager> mUsersPackageManager = new ArrayMap<>();
+
+    private static final int SYSTEM_APPID = SYSTEM_UID;
+
     private static final int MAX_PERMISSION_UPDATE_LOGS = 40;
     private final SharedLog mPermissionUpdateLogs = new SharedLog(MAX_PERMISSION_UPDATE_LOGS, TAG);
 
@@ -212,6 +216,83 @@
         return targetPermission > currentPermission;
     }
 
+    private List<PackageInfo> getInstalledPackagesAsUser(final UserHandle user) {
+        return mPackageManager.getInstalledPackagesAsUser(GET_PERMISSIONS, user.getIdentifier());
+    }
+
+    private synchronized void updateAllApps(final List<PackageInfo> apps) {
+        for (PackageInfo app : apps) {
+            final int appId = app.applicationInfo != null
+                    ? UserHandle.getAppId(app.applicationInfo.uid) : INVALID_UID;
+            if (appId < 0) {
+                continue;
+            }
+            mAllApps.add(appId);
+        }
+    }
+
+    // Return the network permission for the passed list of apps. Note that this depends on the
+    // current settings of the device (See isUidAllowedOnRestrictedNetworks).
+    private SparseIntArray makeUidsNetworkPerm(final List<PackageInfo> apps) {
+        final SparseIntArray uidsPerm = new SparseIntArray();
+        for (PackageInfo app : apps) {
+            final int uid = app.applicationInfo != null ? app.applicationInfo.uid : INVALID_UID;
+            if (uid < 0) {
+                continue;
+            }
+            final int permission = getPackageNetdNetworkPermission(app);
+            if (isHigherNetworkPermission(permission, uidsPerm.get(uid, PERMISSION_NONE))) {
+                uidsPerm.put(uid, permission);
+            }
+        }
+        return uidsPerm;
+    }
+
+    private static SparseIntArray makeAppIdsTrafficPerm(final List<PackageInfo> apps) {
+        final SparseIntArray appIdsPerm = new SparseIntArray();
+        for (PackageInfo app : apps) {
+            final int appId = app.applicationInfo != null
+                    ? UserHandle.getAppId(app.applicationInfo.uid) : INVALID_UID;
+            if (appId < 0) {
+                continue;
+            }
+            final int otherNetdPerms = getNetdPermissionMask(app.requestedPermissions,
+                    app.requestedPermissionsFlags);
+            appIdsPerm.put(appId, appIdsPerm.get(appId) | otherNetdPerms);
+        }
+        return appIdsPerm;
+    }
+
+    private synchronized void updateUidsNetworkPermission(final SparseIntArray uids) {
+        for (int i = 0; i < uids.size(); i++) {
+            mUidToNetworkPerm.put(uids.keyAt(i), uids.valueAt(i));
+        }
+        sendUidsNetworkPermission(uids, true /* add */);
+    }
+
+    private void updateAppIdsTrafficPermission(final SparseIntArray appIds,
+            final SparseIntArray extraAppIds) {
+        for (int i = 0; i < extraAppIds.size(); i++) {
+            final int appId = extraAppIds.keyAt(i);
+            final int permission = extraAppIds.valueAt(i);
+            appIds.put(appId, appIds.get(appId) | permission);
+        }
+        sendAppIdsTrafficPermission(appIds);
+    }
+
+    private SparseIntArray getSystemTrafficPerm() {
+        final SparseIntArray appIdsPerm = new SparseIntArray();
+        for (final int uid : mSystemConfigManager.getSystemPermissionUids(INTERNET)) {
+            final int appId = UserHandle.getAppId(uid);
+            appIdsPerm.put(appId, appIdsPerm.get(appId) | PERMISSION_INTERNET);
+        }
+        for (final int uid : mSystemConfigManager.getSystemPermissionUids(UPDATE_DEVICE_STATS)) {
+            final int appId = UserHandle.getAppId(uid);
+            appIdsPerm.put(appId, appIdsPerm.get(appId) | PERMISSION_UPDATE_DEVICE_STATS);
+        }
+        return appIdsPerm;
+    }
+
     // Intended to be called only once at startup, after the system is ready. Installs a broadcast
     // receiver to monitor ongoing UID changes, so this shouldn't/needn't be called again.
     public synchronized void startMonitoring() {
@@ -252,65 +333,18 @@
         // mUidsAllowedOnRestrictedNetworks.
         updateUidsAllowedOnRestrictedNetworks(mDeps.getUidsAllowedOnRestrictedNetworks(mContext));
 
-        List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS
-                | MATCH_ANY_USER);
-        if (apps == null) {
-            loge("No apps");
-            return;
+        final List<UserHandle> usrs = mUserManager.getUserHandles(true /* excludeDying */);
+        // Update netd permissions for all users.
+        for (UserHandle user : usrs) {
+            onUserAdded(user);
         }
-
-        final SparseIntArray netdPermsAppIds = new SparseIntArray();
-
-        for (PackageInfo app : apps) {
-            int uid = app.applicationInfo != null ? app.applicationInfo.uid : INVALID_UID;
-            if (uid < 0) {
-                continue;
-            }
-            final int appId = UserHandle.getAppId(uid);
-            mAllApps.add(appId);
-
-            final int permission = getPackageNetdNetworkPermission(app);
-            if (isHigherNetworkPermission(permission, mApps.get(appId, PERMISSION_NONE))) {
-                mApps.put(appId, permission);
-            }
-
-            //TODO: unify the management of the permissions into one codepath.
-            int otherNetdPerms = getNetdPermissionMask(app.requestedPermissions,
-                    app.requestedPermissionsFlags);
-            netdPermsAppIds.put(appId, netdPermsAppIds.get(appId) | otherNetdPerms);
-        }
-
-        mUsers.addAll(mUserManager.getUserHandles(true /* excludeDying */));
-
-        final SparseArray<String> netdPermToSystemPerm = new SparseArray<>();
-        netdPermToSystemPerm.put(PERMISSION_INTERNET, INTERNET);
-        netdPermToSystemPerm.put(PERMISSION_UPDATE_DEVICE_STATS, UPDATE_DEVICE_STATS);
-        for (int i = 0; i < netdPermToSystemPerm.size(); i++) {
-            final int netdPermission = netdPermToSystemPerm.keyAt(i);
-            final String systemPermission = netdPermToSystemPerm.valueAt(i);
-            final int[] hasPermissionUids =
-                    mSystemConfigManager.getSystemPermissionUids(systemPermission);
-            for (int j = 0; j < hasPermissionUids.length; j++) {
-                final int appId = UserHandle.getAppId(hasPermissionUids[j]);
-                netdPermsAppIds.put(appId, netdPermsAppIds.get(appId) | netdPermission);
-            }
-        }
-        log("Users: " + mUsers.size() + ", Apps: " + mApps.size());
-        update(mUsers, mApps, true);
-        sendPackagePermissionsToNetd(netdPermsAppIds);
+        log("Users: " + mUsers.size() + ", UidToNetworkPerm: " + mUidToNetworkPerm.size());
     }
 
     @VisibleForTesting
     synchronized void updateUidsAllowedOnRestrictedNetworks(final Set<Integer> uids) {
         mUidsAllowedOnRestrictedNetworks.clear();
-        // This is necessary for the app id to match in isUidAllowedOnRestrictedNetworks, and will
-        // grant the permission to all uids associated with the app ID. This is safe even if the app
-        // is only installed on some users because the uid cannot match some other app – this uid is
-        // in effect not installed and can't be run.
-        // TODO (b/192431153): Change appIds back to uids.
-        for (int uid : uids) {
-            mUidsAllowedOnRestrictedNetworks.add(UserHandle.getAppId(uid));
-        }
+        mUidsAllowedOnRestrictedNetworks.addAll(uids);
     }
 
     @VisibleForTesting
@@ -324,7 +358,8 @@
         return (appInfo.targetSdkVersion < VERSION_Q && isVendorApp(appInfo))
                 // Backward compatibility for b/114245686, on devices that launched before Q daemons
                 // and apps running as the system UID are exempted from this check.
-                || (appInfo.uid == SYSTEM_UID && mDeps.getDeviceFirstSdkInt() < VERSION_Q);
+                || (UserHandle.getAppId(appInfo.uid) == SYSTEM_APPID
+                        && mDeps.getDeviceFirstSdkInt() < VERSION_Q);
     }
 
     @VisibleForTesting
@@ -332,7 +367,7 @@
         if (appInfo == null) return false;
         // Check whether package's uid is in allowed on restricted networks uid list. If so, this
         // uid can have netd system permission.
-        return mUidsAllowedOnRestrictedNetworks.contains(UserHandle.getAppId(appInfo.uid));
+        return mUidsAllowedOnRestrictedNetworks.contains(appInfo.uid);
     }
 
     @VisibleForTesting
@@ -365,33 +400,30 @@
     public synchronized boolean hasUseBackgroundNetworksPermission(final int uid) {
         // Apps with any of the CHANGE_NETWORK_STATE, NETWORK_STACK, CONNECTIVITY_INTERNAL or
         // CONNECTIVITY_USE_RESTRICTED_NETWORKS permission has the permission to use background
-        // networks. mApps contains the result of checks for both hasNetworkPermission and
-        // hasRestrictedNetworkPermission. If uid is in the mApps list that means uid has one of
-        // permissions at least.
-        return mApps.get(UserHandle.getAppId(uid), PERMISSION_NONE) != PERMISSION_NONE;
+        // networks. mUidToNetworkPerm contains the result of checks for hasNetworkPermission and
+        // hasRestrictedNetworkPermission, as well as the list of UIDs allowed on restricted
+        // networks. If uid is in the mUidToNetworkPerm list that means uid has one of permissions
+        // at least.
+        return mUidToNetworkPerm.get(uid, PERMISSION_NONE) != PERMISSION_NONE;
     }
 
     /**
      * Returns whether the given uid has permission to use restricted networks.
      */
     public synchronized boolean hasRestrictedNetworksPermission(int uid) {
-        return PERMISSION_SYSTEM == mApps.get(UserHandle.getAppId(uid), PERMISSION_NONE);
+        return PERMISSION_SYSTEM == mUidToNetworkPerm.get(uid, PERMISSION_NONE);
     }
 
-    private void update(Set<UserHandle> users, SparseIntArray apps, boolean add) {
+    private void sendUidsNetworkPermission(SparseIntArray uids, boolean add) {
         List<Integer> network = new ArrayList<>();
         List<Integer> system = new ArrayList<>();
-        for (int i = 0; i < apps.size(); i++) {
-            final int permission = apps.valueAt(i);
+        for (int i = 0; i < uids.size(); i++) {
+            final int permission = uids.valueAt(i);
             if (PERMISSION_NONE == permission) {
                 continue; // Normally NONE is not stored in this map, but just in case
             }
             List<Integer> list = (PERMISSION_SYSTEM == permission) ? system : network;
-            for (UserHandle user : users) {
-                if (user == null) continue;
-
-                list.add(user.getUid(apps.keyAt(i)));
-            }
+            list.add(uids.keyAt(i));
         }
         try {
             if (add) {
@@ -415,7 +447,19 @@
      */
     public synchronized void onUserAdded(@NonNull UserHandle user) {
         mUsers.add(user);
-        update(Set.of(user), mApps, true);
+
+        final List<PackageInfo> apps = getInstalledPackagesAsUser(user);
+
+        // Save all apps
+        updateAllApps(apps);
+
+        // Uids network permissions
+        final SparseIntArray uids = makeUidsNetworkPerm(apps);
+        updateUidsNetworkPermission(uids);
+
+        // App ids traffic permission
+        final SparseIntArray appIds = makeAppIdsTrafficPerm(apps);
+        updateAppIdsTrafficPermission(appIds, getSystemTrafficPerm());
     }
 
     /**
@@ -427,44 +471,54 @@
      */
     public synchronized void onUserRemoved(@NonNull UserHandle user) {
         mUsers.remove(user);
-        update(Set.of(user), mApps, false);
+
+        final SparseIntArray removedUids = new SparseIntArray();
+        final SparseIntArray allUids = mUidToNetworkPerm.clone();
+        for (int i = 0; i < allUids.size(); i++) {
+            final int uid = allUids.keyAt(i);
+            if (user.equals(UserHandle.getUserHandleForUid(uid))) {
+                mUidToNetworkPerm.delete(uid);
+                removedUids.put(uid, allUids.valueAt(i));
+            }
+        }
+        sendUidsNetworkPermission(removedUids, false /* add */);
     }
 
     /**
      * Compare the current network permission and the given package's permission to find out highest
      * permission for the uid.
      *
+     * @param uid The target uid
      * @param currentPermission Current uid network permission
      * @param name The package has same uid that need compare its permission to update uid network
      *             permission.
      */
     @VisibleForTesting
-    protected int highestPermissionForUid(int currentPermission, String name) {
+    protected int highestPermissionForUid(int uid, int currentPermission, String name) {
+        // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
+        // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
         if (currentPermission == PERMISSION_SYSTEM) {
             return currentPermission;
         }
-        try {
-            final PackageInfo app = mPackageManager.getPackageInfo(name,
-                    GET_PERMISSIONS | MATCH_ANY_USER);
-            final int permission = getPackageNetdNetworkPermission(app);
-            if (isHigherNetworkPermission(permission, currentPermission)) {
-                return permission;
-            }
-        } catch (NameNotFoundException e) {
-            // App not found.
-            loge("NameNotFoundException " + name);
+        final PackageInfo app = getPackageInfoAsUser(name, UserHandle.getUserHandleForUid(uid));
+        if (app == null) return currentPermission;
+
+        final int permission = getPackageNetdNetworkPermission(app);
+        if (isHigherNetworkPermission(permission, currentPermission)) {
+            return permission;
         }
         return currentPermission;
     }
 
-    private int getPermissionForUid(final int uid) {
+    private int getTrafficPermissionForUid(final int uid) {
         int permission = PERMISSION_NONE;
         // Check all the packages for this UID. The UID has the permission if any of the
         // packages in it has the permission.
         final String[] packages = mPackageManager.getPackagesForUid(uid);
         if (packages != null && packages.length > 0) {
             for (String name : packages) {
-                final PackageInfo app = getPackageInfo(name);
+                final PackageInfo app = getPackageInfoAsUser(name,
+                        UserHandle.getUserHandleForUid(uid));
                 if (app != null && app.requestedPermissions != null) {
                     permission |= getNetdPermissionMask(app.requestedPermissions,
                             app.requestedPermissionsFlags);
@@ -524,24 +578,23 @@
      */
     public synchronized void onPackageAdded(@NonNull final String packageName, final int uid) {
         final int appId = UserHandle.getAppId(uid);
-        final int trafficPerm = getPermissionForUid(uid);
+        final int trafficPerm = getTrafficPermissionForUid(uid);
         sendPackagePermissionsForAppId(appId, trafficPerm);
 
-        // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
-        // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
-        final int currentPermission = mApps.get(appId, PERMISSION_NONE);
-        final int permission = highestPermissionForUid(currentPermission, packageName);
+        final int currentPermission = mUidToNetworkPerm.get(uid, PERMISSION_NONE);
+        final int permission = highestPermissionForUid(uid, currentPermission, packageName);
         if (permission != currentPermission) {
-            mApps.put(appId, permission);
+            mUidToNetworkPerm.put(uid, permission);
 
             SparseIntArray apps = new SparseIntArray();
-            apps.put(appId, permission);
-            update(mUsers, apps, true);
+            apps.put(uid, permission);
+            sendUidsNetworkPermission(apps, true /* add */);
         }
 
         // If the newly-installed package falls within some VPN's uid range, update Netd with it.
-        // This needs to happen after the mApps update above, since removeBypassingUids() in
-        // updateVpnUid() depends on mApps to check if the package can bypass VPN.
+        // This needs to happen after the mUidToNetworkPerm update above, since
+        // removeBypassingUids() in updateVpnUid() depends on mUidToNetworkPerm to check if the
+        // package can bypass VPN.
         updateVpnUid(uid, true /* add */);
         mAllApps.add(appId);
         mPermissionUpdateLogs.log("Package add: name=" + packageName + ", uid=" + uid
@@ -557,7 +610,7 @@
             for (String name : packages) {
                 // If multiple packages have the same UID, give the UID all permissions that
                 // any package in that UID has.
-                permission = highestPermissionForUid(permission, name);
+                permission = highestPermissionForUid(uid, permission, name);
                 if (permission == PERMISSION_SYSTEM) {
                     break;
                 }
@@ -576,48 +629,42 @@
      */
     public synchronized void onPackageRemoved(@NonNull final String packageName, final int uid) {
         final int appId = UserHandle.getAppId(uid);
-        final int trafficPerm = getPermissionForUid(uid);
+        final int trafficPerm = getTrafficPermissionForUid(uid);
         sendPackagePermissionsForAppId(appId, trafficPerm);
 
         // If the newly-removed package falls within some VPN's uid range, update Netd with it.
-        // This needs to happen before the mApps update below, since removeBypassingUids() in
-        // updateVpnUid() depends on mApps to check if the package can bypass VPN.
+        // This needs to happen before the mUidToNetworkPerm update below, since
+        // removeBypassingUids() in updateVpnUid() depends on mUidToNetworkPerm to check if the
+        // package can bypass VPN.
         updateVpnUid(uid, false /* add */);
         // If the package has been removed from all users on the device, clear it form mAllApps.
         if (mPackageManager.getNameForUid(uid) == null) {
             mAllApps.remove(appId);
         }
 
-        final int currentPermission = mApps.get(appId, PERMISSION_NONE);
+        final int currentPermission = mUidToNetworkPerm.get(uid, PERMISSION_NONE);
         final int permission = highestUidNetworkPermission(uid);
         mPermissionUpdateLogs.log("Package remove: name=" + packageName + ", uid=" + uid
                 + ", nPerm=(" + permissionToString(permission) + "/"
                 + permissionToString(currentPermission) + ")"
                 + ", tPerm=" + permissionToString(trafficPerm));
-        if (permission == PERMISSION_SYSTEM) {
-            // An app with this UID still has the SYSTEM permission.
-            // Therefore, this UID must already have the SYSTEM permission.
-            // Nothing to do.
-            return;
-        }
-        // If the permissions of this UID have not changed, do nothing.
-        if (permission == currentPermission) return;
-
-        final SparseIntArray apps = new SparseIntArray();
-        if (permission != PERMISSION_NONE) {
-            mApps.put(appId, permission);
-            apps.put(appId, permission);
-            update(mUsers, apps, true);
-        } else {
-            mApps.delete(appId);
-            apps.put(appId, PERMISSION_NETWORK);  // doesn't matter which permission we pick here
-            update(mUsers, apps, false);
+        if (permission != currentPermission) {
+            final SparseIntArray apps = new SparseIntArray();
+            if (permission == PERMISSION_NONE) {
+                mUidToNetworkPerm.delete(uid);
+                apps.put(uid, PERMISSION_NETWORK);  // doesn't matter which permission we pick here
+                sendUidsNetworkPermission(apps, false);
+            } else {
+                mUidToNetworkPerm.put(uid, permission);
+                apps.put(uid, permission);
+                sendUidsNetworkPermission(apps, true);
+            }
         }
     }
 
     private static int getNetdPermissionMask(String[] requestedPermissions,
                                              int[] requestedPermissionsFlags) {
-        int permissions = 0;
+        int permissions = PERMISSION_NONE;
         if (requestedPermissions == null || requestedPermissionsFlags == null) return permissions;
         for (int i = 0; i < requestedPermissions.length; i++) {
             if (requestedPermissions[i].equals(INTERNET)
@@ -632,12 +679,23 @@
         return permissions;
     }
 
-    private PackageInfo getPackageInfo(String packageName) {
+    private synchronized PackageManager getPackageManagerAsUser(UserHandle user) {
+        PackageManager pm = mUsersPackageManager.get(user);
+        if (pm == null) {
+            pm = mContext.createContextAsUser(user, 0 /* flag */).getPackageManager();
+            mUsersPackageManager.put(user, pm);
+        }
+        return pm;
+    }
+
+    private PackageInfo getPackageInfoAsUser(String packageName, UserHandle user) {
         try {
-            PackageInfo app = mPackageManager.getPackageInfo(packageName, GET_PERMISSIONS
-                    | MATCH_ANY_USER);
-            return app;
+            final PackageInfo info = getPackageManagerAsUser(user)
+                    .getPackageInfo(packageName, GET_PERMISSIONS);
+            return info;
         } catch (NameNotFoundException e) {
+            // App not found.
+            loge("NameNotFoundException " + packageName);
             return null;
         }
     }
@@ -725,8 +783,7 @@
      */
     private void removeBypassingUids(Set<Integer> uids, int vpnAppUid) {
         uids.remove(vpnAppUid);
-        uids.removeIf(uid ->
-                mApps.get(UserHandle.getAppId(uid), PERMISSION_NONE) == PERMISSION_SYSTEM);
+        uids.removeIf(uid -> mUidToNetworkPerm.get(uid, PERMISSION_NONE) == PERMISSION_SYSTEM);
     }
 
     /**
@@ -773,7 +830,7 @@
     void sendPackagePermissionsForAppId(int appId, int permissions) {
         SparseIntArray netdPermissionsAppIds = new SparseIntArray();
         netdPermissionsAppIds.put(appId, permissions);
-        sendPackagePermissionsToNetd(netdPermissionsAppIds);
+        sendAppIdsTrafficPermission(netdPermissionsAppIds);
     }
 
     /**
@@ -785,16 +842,16 @@
      * @hide
      */
     @VisibleForTesting
-    void sendPackagePermissionsToNetd(SparseIntArray netdPermissionsAppIds) {
+    void sendAppIdsTrafficPermission(SparseIntArray netdPermissionsAppIds) {
         if (mNetd == null) {
             Log.e(TAG, "Failed to get the netd service");
             return;
         }
-        ArrayList<Integer> allPermissionAppIds = new ArrayList<>();
-        ArrayList<Integer> internetPermissionAppIds = new ArrayList<>();
-        ArrayList<Integer> updateStatsPermissionAppIds = new ArrayList<>();
-        ArrayList<Integer> noPermissionAppIds = new ArrayList<>();
-        ArrayList<Integer> uninstalledAppIds = new ArrayList<>();
+        final ArrayList<Integer> allPermissionAppIds = new ArrayList<>();
+        final ArrayList<Integer> internetPermissionAppIds = new ArrayList<>();
+        final ArrayList<Integer> updateStatsPermissionAppIds = new ArrayList<>();
+        final ArrayList<Integer> noPermissionAppIds = new ArrayList<>();
+        final ArrayList<Integer> uninstalledAppIds = new ArrayList<>();
         for (int i = 0; i < netdPermissionsAppIds.size(); i++) {
             int permissions = netdPermissionsAppIds.valueAt(i);
             switch(permissions) {
@@ -865,20 +922,19 @@
         for (Integer uid : uidsToUpdate) {
             final int permission = highestUidNetworkPermission(uid);
 
-            final int appId = UserHandle.getAppId(uid);
             if (PERMISSION_NONE == permission) {
                 // Doesn't matter which permission is set here.
-                removedUids.put(appId, PERMISSION_NETWORK);
-                mApps.delete(appId);
+                removedUids.put(uid, PERMISSION_NETWORK);
+                mUidToNetworkPerm.delete(uid);
             } else {
-                updatedUids.put(appId, permission);
-                mApps.put(appId, permission);
+                updatedUids.put(uid, permission);
+                mUidToNetworkPerm.put(uid, permission);
             }
         }
 
         // Step3. Update or revoke permission for uids with netd.
-        update(mUsers, updatedUids, true /* add */);
-        update(mUsers, removedUids, false /* add */);
+        sendUidsNetworkPermission(updatedUids, true /* add */);
+        sendUidsNetworkPermission(removedUids, false /* add */);
         mPermissionUpdateLogs.log("Setting change: update=" + updatedUids
                 + ", remove=" + removedUids);
     }
@@ -890,11 +946,13 @@
         }
 
         for (String app : pkgList) {
-            final PackageInfo info = getPackageInfo(app);
-            if (info == null || info.applicationInfo == null) continue;
+            for (UserHandle user : mUsers) {
+                final PackageInfo info = getPackageInfoAsUser(app, user);
+                if (info == null || info.applicationInfo == null) continue;
 
-            final int appId = info.applicationInfo.uid;
-            onPackageAdded(app, appId); // Use onPackageAdded to add package one by one.
+                final int uid = info.applicationInfo.uid;
+                onPackageAdded(app, uid); // Use onPackageAdded to add package one by one.
+            }
         }
     }
 
diff --git a/tests/common/java/android/net/NetworkCapabilitiesTest.java b/tests/common/java/android/net/NetworkCapabilitiesTest.java
index 27a3cc2..32f00a3 100644
--- a/tests/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/common/java/android/net/NetworkCapabilitiesTest.java
@@ -285,19 +285,11 @@
             assertFalse(netCap2.satisfiedByUids(netCap));
             assertFalse(netCap.appliesToUid(650));
             assertTrue(netCap2.appliesToUid(650));
-            netCap.combineCapabilities(netCap2);
+            netCap.setUids(uids);
             assertTrue(netCap2.satisfiedByUids(netCap));
             assertTrue(netCap.appliesToUid(650));
             assertFalse(netCap.appliesToUid(500));
 
-            assertTrue(new NetworkCapabilities().satisfiedByUids(netCap));
-            netCap.combineCapabilities(new NetworkCapabilities());
-            assertTrue(netCap.appliesToUid(500));
-            assertTrue(netCap.appliesToUidRange(new UidRange(1, 100000)));
-            assertFalse(netCap2.appliesToUid(500));
-            assertFalse(netCap2.appliesToUidRange(new UidRange(1, 100000)));
-            assertTrue(new NetworkCapabilities().satisfiedByUids(netCap));
-
             // Null uids satisfies everything.
             netCap.setUids(null);
             assertTrue(netCap2.satisfiedByUids(netCap));
@@ -590,103 +582,6 @@
     }
 
     @Test
-    public void testCombineCapabilities() {
-        NetworkCapabilities nc1 = new NetworkCapabilities();
-        NetworkCapabilities nc2 = new NetworkCapabilities();
-
-        if (isAtLeastS()) {
-            nc1.addForbiddenCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
-        }
-        nc1.addCapability(NET_CAPABILITY_NOT_ROAMING);
-        assertNotEquals(nc1, nc2);
-        nc2.combineCapabilities(nc1);
-        assertEquals(nc1, nc2);
-        assertTrue(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
-        if (isAtLeastS()) {
-            assertTrue(nc2.hasForbiddenCapability(NET_CAPABILITY_CAPTIVE_PORTAL));
-        }
-
-        if (isAtLeastS()) {
-            // This will effectively move NOT_ROAMING capability from required to forbidden for nc1.
-            nc1.addForbiddenCapability(NET_CAPABILITY_NOT_ROAMING);
-            // It is not allowed to have the same capability in both wanted and forbidden list.
-            assertThrows(IllegalArgumentException.class, () -> nc2.combineCapabilities(nc1));
-            // Remove forbidden capability to continue other tests.
-            nc1.removeForbiddenCapability(NET_CAPABILITY_NOT_ROAMING);
-        }
-
-        nc1.setSSID(TEST_SSID);
-        nc2.combineCapabilities(nc1);
-        if (isAtLeastR()) {
-            assertTrue(TEST_SSID.equals(nc2.getSsid()));
-        }
-
-        // Because they now have the same SSID, the following call should not throw
-        nc2.combineCapabilities(nc1);
-
-        nc1.setSSID(DIFFERENT_TEST_SSID);
-        try {
-            nc2.combineCapabilities(nc1);
-            fail("Expected IllegalStateException: can't combine different SSIDs");
-        } catch (IllegalStateException expected) {}
-        nc1.setSSID(TEST_SSID);
-
-        if (isAtLeastS()) {
-            nc1.setUids(uidRanges(10, 13));
-            assertNotEquals(nc1, nc2);
-            nc2.combineCapabilities(nc1);  // Everything + 10~13 is still everything.
-            assertNotEquals(nc1, nc2);
-            nc1.combineCapabilities(nc2);  // 10~13 + everything is everything.
-            assertEquals(nc1, nc2);
-            nc1.setUids(uidRanges(10, 13));
-            nc2.setUids(uidRanges(20, 23));
-            assertNotEquals(nc1, nc2);
-            nc1.combineCapabilities(nc2);
-            assertTrue(nc1.appliesToUid(12));
-            assertFalse(nc2.appliesToUid(12));
-            assertTrue(nc1.appliesToUid(22));
-            assertTrue(nc2.appliesToUid(22));
-
-            // Verify the subscription id list can be combined only when they are equal.
-            nc1.setSubscriptionIds(Set.of(TEST_SUBID1, TEST_SUBID2));
-            nc2.setSubscriptionIds(Set.of(TEST_SUBID2));
-            assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1));
-
-            nc2.setSubscriptionIds(Set.of());
-            assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1));
-
-            nc2.setSubscriptionIds(Set.of(TEST_SUBID2, TEST_SUBID1));
-            nc2.combineCapabilities(nc1);
-            assertEquals(Set.of(TEST_SUBID2, TEST_SUBID1), nc2.getSubscriptionIds());
-        }
-    }
-
-    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
-    public void testCombineCapabilities_AdministratorUids() {
-        final NetworkCapabilities nc1 = new NetworkCapabilities();
-        final NetworkCapabilities nc2 = new NetworkCapabilities();
-
-        final int[] adminUids = {3, 6, 12};
-        nc1.setAdministratorUids(adminUids);
-        nc2.combineCapabilities(nc1);
-        assertTrue(nc2.equalsAdministratorUids(nc1));
-        assertArrayEquals(nc2.getAdministratorUids(), adminUids);
-
-        final int[] adminUidsOtherOrder = {3, 12, 6};
-        nc1.setAdministratorUids(adminUidsOtherOrder);
-        assertTrue(nc2.equalsAdministratorUids(nc1));
-
-        final int[] adminUids2 = {11, 1, 12, 3, 6};
-        nc1.setAdministratorUids(adminUids2);
-        assertFalse(nc2.equalsAdministratorUids(nc1));
-        assertFalse(Arrays.equals(nc2.getAdministratorUids(), adminUids2));
-        try {
-            nc2.combineCapabilities(nc1);
-            fail("Shouldn't be able to combine different lists of admin UIDs");
-        } catch (IllegalStateException expected) { }
-    }
-
-    @Test
     public void testSetCapabilities() {
         final int[] REQUIRED_CAPABILITIES = new int[] {
                 NET_CAPABILITY_INTERNET, NET_CAPABILITY_NOT_VPN };
@@ -802,29 +697,6 @@
     }
 
     @Test
-    public void testCombineTransportInfo() {
-        NetworkCapabilities nc1 = new NetworkCapabilities();
-        nc1.setTransportInfo(new TestTransportInfo());
-
-        NetworkCapabilities nc2 = new NetworkCapabilities();
-        // new TransportInfo so that object is not #equals to nc1's TransportInfo (that's where
-        // combine fails)
-        nc2.setTransportInfo(new TestTransportInfo());
-
-        try {
-            nc1.combineCapabilities(nc2);
-            fail("Should not be able to combine NetworkCabilities which contain TransportInfos");
-        } catch (IllegalStateException expected) {
-            // empty
-        }
-
-        // verify that can combine with identical TransportInfo objects
-        NetworkCapabilities nc3 = new NetworkCapabilities();
-        nc3.setTransportInfo(nc1.getTransportInfo());
-        nc1.combineCapabilities(nc3);
-    }
-
-    @Test
     public void testSet() {
         NetworkCapabilities nc1 = new NetworkCapabilities();
         NetworkCapabilities nc2 = new NetworkCapabilities();
diff --git a/tests/common/java/android/net/UidRangeTest.java b/tests/common/java/android/net/UidRangeTest.java
index 1b1c954..a435119 100644
--- a/tests/common/java/android/net/UidRangeTest.java
+++ b/tests/common/java/android/net/UidRangeTest.java
@@ -22,11 +22,15 @@
 import static android.os.UserHandle.getUid;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Build;
 import android.os.UserHandle;
+import android.util.ArraySet;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -38,6 +42,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Set;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class UidRangeTest {
@@ -110,4 +116,61 @@
         assertEquals(USER_SYSTEM + 1, uidRangeOfSecondaryUser.getStartUser());
         assertEquals(USER_SYSTEM + 1, uidRangeOfSecondaryUser.getEndUser());
     }
+
+    private static void assertSameUids(@NonNull final String msg, @Nullable final Set<UidRange> s1,
+            @Nullable final Set<UidRange> s2) {
+        assertTrue(msg + " : " + s1 + " unexpectedly different from " + s2,
+                UidRange.hasSameUids(s1, s2));
+    }
+
+    private static void assertDifferentUids(@NonNull final String msg,
+            @Nullable final Set<UidRange> s1, @Nullable final Set<UidRange> s2) {
+        assertFalse(msg + " : " + s1 + " unexpectedly equal to " + s2,
+                UidRange.hasSameUids(s1, s2));
+    }
+
+    // R doesn't have UidRange.hasSameUids, but since S has the module, it does have hasSameUids.
+    @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+    public void testHasSameUids() {
+        final UidRange uids1 = new UidRange(1, 100);
+        final UidRange uids2 = new UidRange(3, 300);
+        final UidRange uids3 = new UidRange(1, 1000);
+        final UidRange uids4 = new UidRange(800, 1000);
+
+        assertSameUids("null <=> null", null, null);
+        final Set<UidRange> set1 = new ArraySet<>();
+        assertDifferentUids("empty <=> null", set1, null);
+        final Set<UidRange> set2 = new ArraySet<>();
+        set1.add(uids1);
+        assertDifferentUids("uids1 <=> null", set1, null);
+        assertDifferentUids("null <=> uids1", null, set1);
+        assertDifferentUids("uids1 <=> empty", set1, set2);
+        set2.add(uids1);
+        assertSameUids("uids1 <=> uids1", set1, set2);
+        set1.add(uids2);
+        assertDifferentUids("uids1,2 <=> uids1", set1, set2);
+        set1.add(uids3);
+        assertDifferentUids("uids1,2,3 <=> uids1", set1, set2);
+        set2.add(uids3);
+        assertDifferentUids("uids1,2,3 <=> uids1,3", set1, set2);
+        set2.add(uids2);
+        assertSameUids("uids1,2,3 <=> uids1,2,3", set1, set2);
+        set1.remove(uids2);
+        assertDifferentUids("uids1,3 <=> uids1,2,3", set1, set2);
+        set1.add(uids4);
+        assertDifferentUids("uids1,3,4 <=> uids1,2,3", set1, set2);
+        set2.add(uids4);
+        assertDifferentUids("uids1,3,4 <=> uids1,2,3,4", set1, set2);
+        assertDifferentUids("uids1,3,4 <=> null", set1, null);
+        set2.remove(uids2);
+        assertSameUids("uids1,3,4 <=> uids1,3,4", set1, set2);
+        set2.remove(uids1);
+        assertDifferentUids("uids1,3,4 <=> uids3,4", set1, set2);
+        set2.remove(uids3);
+        assertDifferentUids("uids1,3,4 <=> uids4", set1, set2);
+        set2.remove(uids4);
+        assertDifferentUids("uids1,3,4 <=> empty", set1, set2);
+        assertDifferentUids("null <=> empty", null, set2);
+        assertSameUids("empty <=> empty", set2, new ArraySet<>());
+    }
 }
diff --git a/tests/cts/hostside/app/Android.bp b/tests/cts/hostside/app/Android.bp
index 674af14..63572c3 100644
--- a/tests/cts/hostside/app/Android.bp
+++ b/tests/cts/hostside/app/Android.bp
@@ -45,5 +45,6 @@
     test_suites: [
         "cts",
         "general-tests",
+        "sts",
     ],
 }
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
index 311b3f0..3abc4fb 100755
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -727,11 +727,15 @@
     @Test
     public void testDefault() throws Exception {
         if (!supportedHardware()) return;
-        // If adb TCP port opened, this test may running by adb over network.
-        // All of socket would be destroyed in this test. So this test don't
-        // support adb over network, see b/119382723.
-        if (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1
-                || SystemProperties.getInt("service.adb.tcp.port", -1) > -1) {
+        if (!SdkLevel.isAtLeastS() && (
+                SystemProperties.getInt("persist.adb.tcp.port", -1) > -1
+                        || SystemProperties.getInt("service.adb.tcp.port", -1) > -1)) {
+            // If adb TCP port opened, this test may running by adb over network.
+            // All of socket would be destroyed in this test. So this test don't
+            // support adb over network, see b/119382723.
+            // This is fixed in S, but still affects previous Android versions,
+            // and this test must be backwards compatible.
+            // TODO: Delete this code entirely when R is no longer supported.
             Log.i(TAG, "adb is running over the network, so skip this test");
             return;
         }
@@ -842,11 +846,16 @@
         FileDescriptor remoteFd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
 
         String disallowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
-        // If adb TCP port opened, this test may running by adb over TCP.
-        // Add com.android.shell appllication into blacklist to exclude adb socket for VPN test,
-        // see b/119382723.
-        // Note: The test don't support running adb over network for root device
-        disallowedApps = disallowedApps + ",com.android.shell";
+        if (!SdkLevel.isAtLeastS()) {
+            // If adb TCP port opened, this test may running by adb over TCP.
+            // Add com.android.shell application into disallowedApps to exclude adb socket for VPN
+            // test, see b/119382723 (the test doesn't support adb over TCP when adb runs as root).
+            //
+            // This is fixed in S, but still affects previous Android versions,
+            // and this test must be backwards compatible.
+            // TODO: Delete this code entirely when R is no longer supported.
+            disallowedApps = disallowedApps + ",com.android.shell";
+        }
         Log.i(TAG, "Append shell app to disallowedApps: " + disallowedApps);
         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
                  new String[] {"192.0.2.0/24", "2001:db8::/32"},
@@ -930,11 +939,17 @@
         if (!supportedHardware()) return;
         ProxyInfo initialProxy = mCM.getDefaultProxy();
 
-        // If adb TCP port opened, this test may running by adb over TCP.
-        // Add com.android.shell appllication into blacklist to exclude adb socket for VPN test,
-        // see b/119382723.
-        // Note: The test don't support running adb over network for root device
-        String disallowedApps = mPackageName + ",com.android.shell";
+        String disallowedApps = mPackageName;
+        if (!SdkLevel.isAtLeastS()) {
+            // If adb TCP port opened, this test may running by adb over TCP.
+            // Add com.android.shell application into disallowedApps to exclude adb socket for VPN
+            // test, see b/119382723 (the test doesn't support adb over TCP when adb runs as root).
+            //
+            // This is fixed in S, but still affects previous Android versions,
+            // and this test must be backwards compatible.
+            // TODO: Delete this code entirely when R is no longer supported.
+            disallowedApps += ",com.android.shell";
+        }
         ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("10.0.0.1", 8888);
         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
                 new String[] {"0.0.0.0/0", "::/0"}, "", disallowedApps,
diff --git a/tests/cts/hostside/app2/Android.bp b/tests/cts/hostside/app2/Android.bp
index dd33eed..4c9bccf 100644
--- a/tests/cts/hostside/app2/Android.bp
+++ b/tests/cts/hostside/app2/Android.bp
@@ -28,6 +28,7 @@
     test_suites: [
         "cts",
         "general-tests",
+        "sts",
     ],
     certificate: ":cts-net-app",
 }
diff --git a/tests/cts/net/OWNERS b/tests/cts/net/OWNERS
index 432bd9b..df5569e 100644
--- a/tests/cts/net/OWNERS
+++ b/tests/cts/net/OWNERS
@@ -1,3 +1,5 @@
 # Bug component: 31808
 # Inherits parent owners
 per-file src/android/net/cts/NetworkWatchlistTest.java=alanstokes@google.com
+
+# Bug component: 685852 = per-file *IpSec*
\ No newline at end of file
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 579be15..80c2db4 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -2380,7 +2380,7 @@
         final String ssid = unquoteSSID(wifiNetworkCapabilities.getSsid());
         final boolean oldMeteredValue = wifiNetworkCapabilities.isMetered();
 
-        try {
+        testAndCleanup(() -> {
             // This network will be used for unmetered. Wait for it to be validated because
             // OEM_NETWORK_PREFERENCE_TEST only prefers NOT_METERED&VALIDATED to a network with
             // TRANSPORT_TEST, like OEM_NETWORK_PREFERENCE_OEM_PAID.
@@ -2405,18 +2405,18 @@
             // callback in any case therefore confirm its receipt before continuing to assure the
             // system is in the expected state.
             waitForAvailable(systemDefaultCallback, TRANSPORT_WIFI);
-        } finally {
+        }, /* cleanup */ () -> {
             // Validate that removing the test network will fallback to the default network.
             runWithShellPermissionIdentity(tnt::teardown);
             defaultCallback.expectCallback(CallbackEntry.LOST, tnt.getNetwork(),
                     NETWORK_CALLBACK_TIMEOUT_MS);
             waitForAvailable(defaultCallback);
-
-            setWifiMeteredStatusAndWait(ssid, oldMeteredValue, false /* waitForValidation */);
-
-            // Cleanup any prior test state from setOemNetworkPreference
-            clearOemNetworkPreference();
-        }
+            }, /* cleanup */ () -> {
+                setWifiMeteredStatusAndWait(ssid, oldMeteredValue, false /* waitForValidation */);
+            }, /* cleanup */ () -> {
+                // Cleanup any prior test state from setOemNetworkPreference
+                clearOemNetworkPreference();
+            });
     }
 
     /**
@@ -2437,29 +2437,30 @@
 
         final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
 
-        try {
+        testAndCleanup(() -> {
             setOemNetworkPreferenceForMyPackage(
                     OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST_ONLY);
             registerTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
             waitForAvailable(defaultCallback, tnt.getNetwork());
             waitForAvailable(systemDefaultCallback, wifiNetwork);
-        } finally {
-            runWithShellPermissionIdentity(tnt::teardown);
-            defaultCallback.expectCallback(CallbackEntry.LOST, tnt.getNetwork(),
-                    NETWORK_CALLBACK_TIMEOUT_MS);
+        }, /* cleanup */ () -> {
+                runWithShellPermissionIdentity(tnt::teardown);
+                defaultCallback.expectCallback(CallbackEntry.LOST, tnt.getNetwork(),
+                        NETWORK_CALLBACK_TIMEOUT_MS);
 
-            // This network preference should only ever use the test network therefore available
-            // should not trigger when the test network goes down (e.g. switch to cellular).
-            defaultCallback.assertNoCallback();
-            // The system default should still be connected to Wi-fi
-            assertEquals(wifiNetwork, systemDefaultCallback.getLastAvailableNetwork());
+                // This network preference should only ever use the test network therefore available
+                // should not trigger when the test network goes down (e.g. switch to cellular).
+                defaultCallback.assertNoCallback();
+                // The system default should still be connected to Wi-fi
+                assertEquals(wifiNetwork, systemDefaultCallback.getLastAvailableNetwork());
+            }, /* cleanup */ () -> {
+                // Cleanup any prior test state from setOemNetworkPreference
+                clearOemNetworkPreference();
 
-            // Cleanup any prior test state from setOemNetworkPreference
-            clearOemNetworkPreference();
-
-            // The default (non-test) network should be available as the network pref was cleared.
-            waitForAvailable(defaultCallback);
-        }
+                // The default (non-test) network should be available as the network pref was
+                // cleared.
+                waitForAvailable(defaultCallback);
+            });
     }
 
     private void registerTestOemNetworkPreferenceCallbacks(
diff --git a/tests/cts/net/src/android/net/cts/DhcpOptionTest.kt b/tests/cts/net/src/android/net/cts/DhcpOptionTest.kt
new file mode 100644
index 0000000..1a62560
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/DhcpOptionTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 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 android.os.Build
+import android.net.DhcpOption
+import androidx.test.filters.SmallTest
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.runner.RunWith
+import org.junit.Test
+
+@SmallTest
+@IgnoreUpTo(Build.VERSION_CODES.S)
+@RunWith(DevSdkIgnoreRunner::class)
+class DhcpOptionTest {
+    private val DHCP_OPTION_TYPE: Byte = 2
+    private val DHCP_OPTION_VALUE = byteArrayOf(0, 1, 2, 4, 8, 16)
+
+    @Test
+    fun testConstructor() {
+        val dhcpOption = DhcpOption(DHCP_OPTION_TYPE, DHCP_OPTION_VALUE)
+        assertEquals(DHCP_OPTION_TYPE, dhcpOption.type)
+        assertArrayEquals(DHCP_OPTION_VALUE, dhcpOption.value)
+    }
+
+    @Test
+    fun testConstructorWithNullValue() {
+        val dhcpOption = DhcpOption(DHCP_OPTION_TYPE, null)
+        assertEquals(DHCP_OPTION_TYPE, dhcpOption.type)
+        assertNull(dhcpOption.value)
+    }
+}
\ No newline at end of file
diff --git a/tests/cts/net/src/android/net/cts/DnsResolverTest.java b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
index 22168b3..4992795 100644
--- a/tests/cts/net/src/android/net/cts/DnsResolverTest.java
+++ b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
@@ -25,32 +25,46 @@
 import static android.net.cts.util.CtsNetUtils.TestNetworkCallback;
 import static android.system.OsConstants.ETIMEDOUT;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.Context;
 import android.content.ContentResolver;
+import android.content.Context;
 import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
 import android.net.DnsResolver;
-import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
 import android.net.ParseException;
 import android.net.cts.util.CtsNetUtils;
+import android.os.Build;
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.Looper;
 import android.platform.test.annotations.AppModeFull;
-import android.provider.Settings;
 import android.system.ErrnoException;
-import android.test.AndroidTestCase;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.net.module.util.DnsPacket;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 import com.android.testutils.SkipPresubmit;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
@@ -61,7 +75,11 @@
 import java.util.concurrent.TimeUnit;
 
 @AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
-public class DnsResolverTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class DnsResolverTest {
+    @Rule
+    public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+
     private static final String TAG = "DnsResolverTest";
     private static final char[] HEX_CHARS = {
             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
@@ -91,6 +109,7 @@
     static final int QUERY_TIMES = 10;
     static final int NXDOMAIN = 3;
 
+    private Context mContext;
     private ContentResolver mCR;
     private ConnectivityManager mCM;
     private PackageManager mPackageManager;
@@ -99,30 +118,27 @@
     private Executor mExecutorInline;
     private DnsResolver mDns;
 
-    private String mOldMode;
-    private String mOldDnsSpecifier;
     private TestNetworkCallback mWifiRequestCallback = null;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mCM = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getContext();
+        mCM = mContext.getSystemService(ConnectivityManager.class);
         mDns = DnsResolver.getInstance();
         mExecutor = new Handler(Looper.getMainLooper())::post;
         mExecutorInline = (Runnable r) -> r.run();
-        mCR = getContext().getContentResolver();
-        mCtsNetUtils = new CtsNetUtils(getContext());
+        mCR = mContext.getContentResolver();
+        mCtsNetUtils = new CtsNetUtils(mContext);
         mCtsNetUtils.storePrivateDnsSetting();
         mPackageManager = mContext.getPackageManager();
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
         mCtsNetUtils.restorePrivateDnsSetting();
         if (mWifiRequestCallback != null) {
             mCM.unregisterNetworkCallback(mWifiRequestCallback);
         }
-        super.tearDown();
     }
 
     private static String byteArrayToHexString(byte[] bytes) {
@@ -298,42 +314,52 @@
         }
     }
 
+    @Test
     public void testRawQuery() throws Exception {
         doTestRawQuery(mExecutor);
     }
 
+    @Test
     public void testRawQueryInline() throws Exception {
         doTestRawQuery(mExecutorInline);
     }
 
+    @Test
     public void testRawQueryBlob() throws Exception {
         doTestRawQueryBlob(mExecutor);
     }
 
+    @Test
     public void testRawQueryBlobInline() throws Exception {
         doTestRawQueryBlob(mExecutorInline);
     }
 
+    @Test
     public void testRawQueryRoot() throws Exception {
         doTestRawQueryRoot(mExecutor);
     }
 
+    @Test
     public void testRawQueryRootInline() throws Exception {
         doTestRawQueryRoot(mExecutorInline);
     }
 
+    @Test
     public void testRawQueryNXDomain() throws Exception {
         doTestRawQueryNXDomain(mExecutor);
     }
 
+    @Test
     public void testRawQueryNXDomainInline() throws Exception {
         doTestRawQueryNXDomain(mExecutorInline);
     }
 
+    @Test
     public void testRawQueryNXDomainWithPrivateDns() throws Exception {
         doTestRawQueryNXDomainWithPrivateDns(mExecutor);
     }
 
+    @Test
     public void testRawQueryNXDomainInlineWithPrivateDns() throws Exception {
         doTestRawQueryNXDomainWithPrivateDns(mExecutorInline);
     }
@@ -436,6 +462,7 @@
         }
     }
 
+    @Test
     public void testRawQueryCancel() throws InterruptedException {
         final String msg = "Test cancel RawQuery " + TEST_DOMAIN;
         // Start a DNS query and the cancel it immediately. Use VerifyCancelCallback to expect
@@ -465,6 +492,7 @@
         }
     }
 
+    @Test
     public void testRawQueryBlobCancel() throws InterruptedException {
         final String msg = "Test cancel RawQuery blob " + byteArrayToHexString(TEST_BLOB);
         // Start a DNS query and the cancel it immediately. Use VerifyCancelCallback to expect
@@ -493,6 +521,7 @@
         }
     }
 
+    @Test
     public void testCancelBeforeQuery() throws InterruptedException {
         final String msg = "Test cancelled RawQuery " + TEST_DOMAIN;
         for (Network network : getTestableNetworks()) {
@@ -578,34 +607,42 @@
         }
     }
 
+    @Test
     public void testQueryForInetAddress() throws Exception {
         doTestQueryForInetAddress(mExecutor);
     }
 
+    @Test
     public void testQueryForInetAddressInline() throws Exception {
         doTestQueryForInetAddress(mExecutorInline);
     }
 
+    @Test
     public void testQueryForInetAddressIpv4() throws Exception {
         doTestQueryForInetAddressIpv4(mExecutor);
     }
 
+    @Test
     public void testQueryForInetAddressIpv4Inline() throws Exception {
         doTestQueryForInetAddressIpv4(mExecutorInline);
     }
 
+    @Test
     public void testQueryForInetAddressIpv6() throws Exception {
         doTestQueryForInetAddressIpv6(mExecutor);
     }
 
+    @Test
     public void testQueryForInetAddressIpv6Inline() throws Exception {
         doTestQueryForInetAddressIpv6(mExecutorInline);
     }
 
+    @Test
     public void testContinuousQueries() throws Exception {
         doTestContinuousQueries(mExecutor);
     }
 
+    @Test
     @SkipPresubmit(reason = "Flaky: b/159762682; add to presubmit after fixing")
     public void testContinuousQueriesInline() throws Exception {
         doTestContinuousQueries(mExecutorInline);
@@ -625,6 +662,7 @@
         }
     }
 
+    @Test
     public void testQueryCancelForInetAddress() throws InterruptedException {
         final String msg = "Test cancel query for InetAddress " + TEST_DOMAIN;
         // Start a DNS query and the cancel it immediately. Use VerifyCancelInetAddressCallback to
@@ -686,6 +724,7 @@
         }
     }
 
+    @Test
     public void testPrivateDnsBypass() throws InterruptedException {
         final Network[] testNetworks = getTestableNetworks();
 
@@ -773,4 +812,19 @@
             }
         }
     }
+
+    /** Verifies that DnsResolver.DnsException can be subclassed and its constructor re-used. */
+    @Test @IgnoreUpTo(Build.VERSION_CODES.S)
+    public void testDnsExceptionConstructor() throws InterruptedException {
+        class TestDnsException extends DnsResolver.DnsException {
+            TestDnsException(int code, @Nullable Throwable cause) {
+                super(code, cause);
+            }
+        }
+        try {
+            throw new TestDnsException(DnsResolver.ERROR_SYSTEM, null);
+        } catch (DnsResolver.DnsException e) {
+            assertEquals(DnsResolver.ERROR_SYSTEM, e.code);
+        }
+    }
 }
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 1a131d8..ef5dc77 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -61,20 +61,6 @@
 import android.net.Uri
 import android.net.VpnManager
 import android.net.VpnTransportInfo
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnAddKeepalivePacketFilter
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnAutomaticReconnectDisabled
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnBandwidthUpdateRequested
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnNetworkCreated
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnNetworkDestroyed
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnNetworkUnwanted
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnRegisterQosCallback
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnRemoveKeepalivePacketFilter
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnSaveAcceptUnvalidated
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnSignalStrengthThresholdsUpdated
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnStartSocketKeepalive
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnStopSocketKeepalive
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnUnregisterQosCallback
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnValidationStatus
 import android.net.cts.NetworkAgentTest.TestableQosCallback.CallbackEntry.OnError
 import android.net.cts.NetworkAgentTest.TestableQosCallback.CallbackEntry.OnQosSessionAvailable
 import android.net.cts.NetworkAgentTest.TestableQosCallback.CallbackEntry.OnQosSessionLost
@@ -98,6 +84,20 @@
 import com.android.testutils.RecorderCallback.CallbackEntry.Available
 import com.android.testutils.RecorderCallback.CallbackEntry.Losing
 import com.android.testutils.RecorderCallback.CallbackEntry.Lost
+import com.android.testutils.TestableNetworkAgent
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnAddKeepalivePacketFilter
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnAutomaticReconnectDisabled
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnBandwidthUpdateRequested
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkCreated
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkDestroyed
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkUnwanted
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnRegisterQosCallback
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnRemoveKeepalivePacketFilter
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnSaveAcceptUnvalidated
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnStartSocketKeepalive
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnStopSocketKeepalive
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnUnregisterQosCallback
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnValidationStatus
 import com.android.testutils.TestableNetworkCallback
 import org.junit.After
 import org.junit.Assert.assertArrayEquals
@@ -136,10 +136,6 @@
 // and then there is the Binder call), so have a short timeout for this as it will be
 // exhausted every time.
 private const val NO_CALLBACK_TIMEOUT = 200L
-// Any legal score (0~99) for the test network would do, as it is going to be kept up by the
-// requests filed by the test and should never match normal internet requests. 70 is the default
-// score of Ethernet networks, it's as good a value as any other.
-private const val TEST_NETWORK_SCORE = 70
 private const val WORSE_NETWORK_SCORE = 65
 private const val BETTER_NETWORK_SCORE = 75
 private const val FAKE_NET_ID = 1098
@@ -165,10 +161,6 @@
     private val mCM = realContext.getSystemService(ConnectivityManager::class.java)!!
     private val mHandlerThread = HandlerThread("${javaClass.simpleName} handler thread")
     private val mFakeConnectivityService = FakeConnectivityService()
-
-    private class Provider(context: Context, looper: Looper) :
-            NetworkProvider(context, looper, "NetworkAgentTest NetworkProvider")
-
     private val agentsToCleanUp = mutableListOf<NetworkAgent>()
     private val callbacksToCleanUp = mutableListOf<TestableNetworkCallback>()
     private var qosTestSocket: Socket? = null
@@ -219,146 +211,6 @@
         fun disconnect() = agent.onDisconnected()
     }
 
-    private open class TestableNetworkAgent(
-        context: Context,
-        looper: Looper,
-        val nc: NetworkCapabilities,
-        val lp: LinkProperties,
-        conf: NetworkAgentConfig
-    ) : NetworkAgent(context, looper, TestableNetworkAgent::class.java.simpleName /* tag */,
-            nc, lp, TEST_NETWORK_SCORE, conf, Provider(context, looper)) {
-        private val history = ArrayTrackRecord<CallbackEntry>().newReadHead()
-
-        sealed class CallbackEntry {
-            object OnBandwidthUpdateRequested : CallbackEntry()
-            object OnNetworkUnwanted : CallbackEntry()
-            data class OnAddKeepalivePacketFilter(
-                val slot: Int,
-                val packet: KeepalivePacketData
-            ) : CallbackEntry()
-            data class OnRemoveKeepalivePacketFilter(val slot: Int) : CallbackEntry()
-            data class OnStartSocketKeepalive(
-                val slot: Int,
-                val interval: Int,
-                val packet: KeepalivePacketData
-            ) : CallbackEntry()
-            data class OnStopSocketKeepalive(val slot: Int) : CallbackEntry()
-            data class OnSaveAcceptUnvalidated(val accept: Boolean) : CallbackEntry()
-            object OnAutomaticReconnectDisabled : CallbackEntry()
-            data class OnValidationStatus(val status: Int, val uri: Uri?) : CallbackEntry()
-            data class OnSignalStrengthThresholdsUpdated(val thresholds: IntArray) : CallbackEntry()
-            object OnNetworkCreated : CallbackEntry()
-            object OnNetworkDestroyed : CallbackEntry()
-            data class OnRegisterQosCallback(
-                val callbackId: Int,
-                val filter: QosFilter
-            ) : CallbackEntry()
-            data class OnUnregisterQosCallback(val callbackId: Int) : CallbackEntry()
-        }
-
-        override fun onBandwidthUpdateRequested() {
-            history.add(OnBandwidthUpdateRequested)
-        }
-
-        override fun onNetworkUnwanted() {
-            history.add(OnNetworkUnwanted)
-        }
-
-        override fun onAddKeepalivePacketFilter(slot: Int, packet: KeepalivePacketData) {
-            history.add(OnAddKeepalivePacketFilter(slot, packet))
-        }
-
-        override fun onRemoveKeepalivePacketFilter(slot: Int) {
-            history.add(OnRemoveKeepalivePacketFilter(slot))
-        }
-
-        override fun onStartSocketKeepalive(
-            slot: Int,
-            interval: Duration,
-            packet: KeepalivePacketData
-        ) {
-            history.add(OnStartSocketKeepalive(slot, interval.seconds.toInt(), packet))
-        }
-
-        override fun onStopSocketKeepalive(slot: Int) {
-            history.add(OnStopSocketKeepalive(slot))
-        }
-
-        override fun onSaveAcceptUnvalidated(accept: Boolean) {
-            history.add(OnSaveAcceptUnvalidated(accept))
-        }
-
-        override fun onAutomaticReconnectDisabled() {
-            history.add(OnAutomaticReconnectDisabled)
-        }
-
-        override fun onSignalStrengthThresholdsUpdated(thresholds: IntArray) {
-            history.add(OnSignalStrengthThresholdsUpdated(thresholds))
-        }
-
-        fun expectSignalStrengths(thresholds: IntArray? = intArrayOf()) {
-            expectCallback<OnSignalStrengthThresholdsUpdated>().let {
-                assertArrayEquals(thresholds, it.thresholds)
-            }
-        }
-
-        override fun onQosCallbackRegistered(qosCallbackId: Int, filter: QosFilter) {
-            history.add(OnRegisterQosCallback(qosCallbackId, filter))
-        }
-
-        override fun onQosCallbackUnregistered(qosCallbackId: Int) {
-            history.add(OnUnregisterQosCallback(qosCallbackId))
-        }
-
-        override fun onValidationStatus(status: Int, uri: Uri?) {
-            history.add(OnValidationStatus(status, uri))
-        }
-
-        override fun onNetworkCreated() {
-            history.add(OnNetworkCreated)
-        }
-
-        override fun onNetworkDestroyed() {
-            history.add(OnNetworkDestroyed)
-        }
-
-        // Expects the initial validation event that always occurs immediately after registering
-        // a NetworkAgent whose network does not require validation (which test networks do
-        // not, since they lack the INTERNET capability). It always contains the default argument
-        // for the URI.
-        fun expectValidationBypassedStatus() = expectCallback<OnValidationStatus>().let {
-            assertEquals(it.status, VALID_NETWORK)
-            // The returned Uri is parsed from the empty string, which means it's an
-            // instance of the (private) Uri.StringUri. There are no real good ways
-            // to check this, the least bad is to just convert it to a string and
-            // make sure it's empty.
-            assertEquals("", it.uri.toString())
-        }
-
-        inline fun <reified T : CallbackEntry> expectCallback(): T {
-            val foundCallback = history.poll(DEFAULT_TIMEOUT_MS)
-            assertTrue(foundCallback is T, "Expected ${T::class} but found $foundCallback")
-            return foundCallback
-        }
-
-        inline fun <reified T : CallbackEntry> expectCallback(valid: (T) -> Boolean) {
-            val foundCallback = history.poll(DEFAULT_TIMEOUT_MS)
-            assertTrue(foundCallback is T, "Expected ${T::class} but found $foundCallback")
-            assertTrue(valid(foundCallback), "Unexpected callback : $foundCallback")
-        }
-
-        inline fun <reified T : CallbackEntry> eventuallyExpect() =
-                history.poll(DEFAULT_TIMEOUT_MS) { it is T }.also {
-                    assertNotNull(it, "Callback ${T::class} not received")
-        } as T
-
-        fun assertNoCallback() {
-            assertTrue(waitForIdle(DEFAULT_TIMEOUT_MS),
-                    "Handler didn't became idle after ${DEFAULT_TIMEOUT_MS}ms")
-            assertNull(history.peek())
-        }
-    }
-
     private fun requestNetwork(request: NetworkRequest, callback: TestableNetworkCallback) {
         mCM.requestNetwork(request, callback)
         callbacksToCleanUp.add(callback)
diff --git a/tests/cts/net/src/android/net/cts/NetworkValidationTest.kt b/tests/cts/net/src/android/net/cts/NetworkValidationTest.kt
index 5290f0d..8e98dba 100644
--- a/tests/cts/net/src/android/net/cts/NetworkValidationTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkValidationTest.kt
@@ -25,7 +25,6 @@
 import android.net.InetAddresses
 import android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL
 import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
-import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
 import android.net.NetworkCapabilities.TRANSPORT_TEST
 import android.net.NetworkRequest
 import android.net.TestNetworkInterface
@@ -96,7 +95,6 @@
     private val ethRequest = NetworkRequest.Builder()
             // ETHERNET|TEST transport networks do not have NET_CAPABILITY_TRUSTED
             .removeCapability(NET_CAPABILITY_TRUSTED)
-            .addTransportType(TRANSPORT_ETHERNET)
             .addTransportType(TRANSPORT_TEST).build()
     private val ethRequestCb = TestableNetworkCallback()
 
diff --git a/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt b/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt
index dde14ac..391d03a 100644
--- a/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt
@@ -19,12 +19,20 @@
 import android.Manifest
 import android.net.util.NetworkStackUtils
 import android.provider.DeviceConfig
+import android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY
+import android.util.Log
 import com.android.testutils.runAsShell
+import com.android.testutils.tryTest
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.Executor
+import java.util.concurrent.TimeUnit
 
 /**
  * Collection of utility methods for configuring network validation.
  */
 internal object NetworkValidationTestUtil {
+    val TAG = NetworkValidationTestUtil::class.simpleName
+    const val TIMEOUT_MS = 20_000L
 
     /**
      * Clear the test network validation URLs.
@@ -59,10 +67,52 @@
     @JvmStatic fun setUrlExpirationDeviceConfig(timestamp: Long?) =
             setConfig(NetworkStackUtils.TEST_URL_EXPIRATION_TIME, timestamp?.toString())
 
-    private fun setConfig(configKey: String, value: String?) {
-        runAsShell(Manifest.permission.WRITE_DEVICE_CONFIG) {
-            DeviceConfig.setProperty(
-                    DeviceConfig.NAMESPACE_CONNECTIVITY, configKey, value, false /* makeDefault */)
+    private fun setConfig(configKey: String, value: String?): String? {
+        Log.i(TAG, "Setting config \"$configKey\" to \"$value\"")
+        val readWritePermissions = arrayOf(
+                Manifest.permission.READ_DEVICE_CONFIG,
+                Manifest.permission.WRITE_DEVICE_CONFIG)
+
+        val existingValue = runAsShell(*readWritePermissions) {
+            DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY, configKey)
+        }
+        if (existingValue == value) {
+            // Already the correct value. There may be a race if a change is already in flight,
+            // but if multiple threads update the config there is no way to fix that anyway.
+            Log.i(TAG, "\$configKey\" already had value \"$value\"")
+            return value
+        }
+
+        val future = CompletableFuture<String>()
+        val listener = DeviceConfig.OnPropertiesChangedListener {
+            // The listener receives updates for any change to any key, so don't react to
+            // changes that do not affect the relevant key
+            if (!it.keyset.contains(configKey)) return@OnPropertiesChangedListener
+            if (it.getString(configKey, null) == value) {
+                future.complete(value)
+            }
+        }
+
+        return tryTest {
+            runAsShell(*readWritePermissions) {
+                DeviceConfig.addOnPropertiesChangedListener(
+                        NAMESPACE_CONNECTIVITY,
+                        inlineExecutor,
+                        listener)
+                DeviceConfig.setProperty(
+                        NAMESPACE_CONNECTIVITY,
+                        configKey,
+                        value,
+                        false /* makeDefault */)
+                // Don't drop the permission until the config is applied, just in case
+                future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
+            }.also {
+                Log.i(TAG, "Config \"$configKey\" successfully set to \"$value\"")
+            }
+        } cleanup {
+            DeviceConfig.removeOnPropertiesChangedListener(listener)
         }
     }
-}
\ No newline at end of file
+
+    private val inlineExecutor get() = Executor { r -> r.run() }
+}
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 05e40f9..ce873f7 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
@@ -476,6 +476,7 @@
         NetworkCallback callback = new NetworkCallback() {
             @Override
             public void onLinkPropertiesChanged(Network n, LinkProperties lp) {
+                Log.i(TAG, "Link properties of network " + n + " changed to " + lp);
                 if (requiresValidatedServer && lp.getValidatedPrivateDnsServers().isEmpty()) {
                     return;
                 }
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index 38154c2..3735ca4 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -69,8 +69,7 @@
         "java/android/net/IpSecTransformTest.java",
         "java/android/net/KeepalivePacketDataUtilTest.java",
         "java/android/net/NetworkIdentityTest.kt",
-        "java/android/net/NetworkStatsTest.java",
-        "java/android/net/NetworkStatsHistoryTest.java",
+        "java/android/net/NetworkStats*.java",
         "java/android/net/NetworkTemplateTest.kt",
         "java/android/net/TelephonyNetworkSpecifierTest.java",
         "java/android/net/VpnManagerTest.java",
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java b/tests/unit/java/android/net/NetworkStatsAccessTest.java
similarity index 97%
rename from tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java
rename to tests/unit/java/android/net/NetworkStatsAccessTest.java
index 03d9404..0f9ed41 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java
+++ b/tests/unit/java/android/net/NetworkStatsAccessTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -11,10 +11,10 @@
  * 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
+ * limitations under the License.
  */
 
-package com.android.server.net;
+package android.net;
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.when;
@@ -43,7 +43,7 @@
 
 @RunWith(DevSdkIgnoreRunner.class)
 @SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S)
 public class NetworkStatsAccessTest {
     private static final String TEST_PKG = "com.example.test";
     private static final int TEST_UID = 12345;
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/unit/java/android/net/NetworkStatsCollectionTest.java
similarity index 97%
rename from tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java
rename to tests/unit/java/android/net/NetworkStatsCollectionTest.java
index 6b4ead5..0c4ffac 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java
+++ b/tests/unit/java/android/net/NetworkStatsCollectionTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.net;
+package android.net;
 
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.NetworkIdentity.OEM_NONE;
@@ -37,11 +37,6 @@
 import static org.junit.Assert.fail;
 
 import android.content.res.Resources;
-import android.net.ConnectivityManager;
-import android.net.NetworkIdentity;
-import android.net.NetworkStats;
-import android.net.NetworkStatsHistory;
-import android.net.NetworkTemplate;
 import android.os.Build;
 import android.os.Process;
 import android.os.UserHandle;
@@ -83,7 +78,7 @@
  */
 @RunWith(DevSdkIgnoreRunner.class)
 @SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S)
 public class NetworkStatsCollectionTest {
 
     private static final String TEST_FILE = "test.bin";
@@ -250,8 +245,8 @@
                 collection.getRelevantUids(NetworkStatsAccess.Level.DEVICE));
 
         // Verify security check in getHistory.
-        assertNotNull(collection.getHistory(buildTemplateMobileAll(TEST_IMSI), null, myUid, SET_DEFAULT,
-                TAG_NONE, 0, 0L, 0L, NetworkStatsAccess.Level.DEFAULT, myUid));
+        assertNotNull(collection.getHistory(buildTemplateMobileAll(TEST_IMSI), null,
+                myUid, SET_DEFAULT, TAG_NONE, 0, 0L, 0L, NetworkStatsAccess.Level.DEFAULT, myUid));
         try {
             collection.getHistory(buildTemplateMobileAll(TEST_IMSI), null, otherUidInSameUser,
                     SET_DEFAULT, TAG_NONE, 0, 0L, 0L, NetworkStatsAccess.Level.DEFAULT, myUid);
@@ -275,7 +270,8 @@
                 new File(InstrumentationRegistry.getContext().getFilesDir(), TEST_FILE);
         stageFile(R.raw.netstats_v1, testFile);
 
-        final NetworkStatsCollection emptyCollection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS);
+        final NetworkStatsCollection emptyCollection =
+                new NetworkStatsCollection(30 * MINUTE_IN_MILLIS);
         final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS);
         collection.readLegacyNetwork(testFile);
 
@@ -313,7 +309,8 @@
             assertEquals(0L, history.getTotalBytes());
 
             // Normal collection should be untouched
-            history = getHistory(collection, plan, TIME_A, TIME_C); i = 0;
+            history = getHistory(collection, plan, TIME_A, TIME_C);
+            i = 0;
             assertEntry(100647, 197, 23649, 185, history.getValues(i++, null));
             assertEntry(100647, 196, 23648, 185, history.getValues(i++, null));
             assertEntry(18323, 76, 15032, 76, history.getValues(i++, null));
@@ -342,7 +339,8 @@
 
             // Slice from middle should be untouched
             history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS,
-                    TIME_B + HOUR_IN_MILLIS); i = 0;
+                    TIME_B + HOUR_IN_MILLIS);
+            i = 0;
             assertEntry(3821, 23, 4525, 26, history.getValues(i++, null));
             assertEntry(3820, 21, 4524, 26, history.getValues(i++, null));
             assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
@@ -365,7 +363,8 @@
             assertEquals(200000L, history.getTotalBytes());
 
             // Normal collection should be augmented
-            history = getHistory(collection, plan, TIME_A, TIME_C); i = 0;
+            history = getHistory(collection, plan, TIME_A, TIME_C);
+            i = 0;
             assertEntry(100647, 197, 23649, 185, history.getValues(i++, null));
             assertEntry(100647, 196, 23648, 185, history.getValues(i++, null));
             assertEntry(18323, 76, 15032, 76, history.getValues(i++, null));
@@ -397,7 +396,8 @@
 
             // Slice from middle should be augmented
             history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS,
-                    TIME_B + HOUR_IN_MILLIS); i = 0;
+                    TIME_B + HOUR_IN_MILLIS);
+            i = 0;
             assertEntry(2669, 0, 3161, 0, history.getValues(i++, null));
             assertEntry(2668, 0, 3160, 0, history.getValues(i++, null));
             assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
@@ -420,7 +420,8 @@
             assertEquals(400000L, history.getTotalBytes());
 
             // Normal collection should be augmented
-            history = getHistory(collection, plan, TIME_A, TIME_C); i = 0;
+            history = getHistory(collection, plan, TIME_A, TIME_C);
+            i = 0;
             assertEntry(100647, 197, 23649, 185, history.getValues(i++, null));
             assertEntry(100647, 196, 23648, 185, history.getValues(i++, null));
             assertEntry(18323, 76, 15032, 76, history.getValues(i++, null));
@@ -451,7 +452,8 @@
 
             // Slice from middle should be augmented
             history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS,
-                    TIME_B + HOUR_IN_MILLIS); i = 0;
+                    TIME_B + HOUR_IN_MILLIS);
+            i = 0;
             assertEntry(5338, 0, 6322, 0, history.getValues(i++, null));
             assertEntry(5337, 0, 6320, 0, history.getValues(i++, null));
             assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index e76762e..044ff02 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -38,7 +38,6 @@
 import static android.content.pm.PackageManager.FEATURE_WIFI;
 import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
-import static android.content.pm.PackageManager.MATCH_ANY_USER;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
@@ -2006,7 +2005,7 @@
                 buildPackageInfo(/* SYSTEM */ false, APP1_UID),
                 buildPackageInfo(/* SYSTEM */ false, APP2_UID),
                 buildPackageInfo(/* SYSTEM */ false, VPN_UID)
-        })).when(mPackageManager).getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER));
+        })).when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
 
         // Create a fake always-on VPN package.
         final int userId = UserHandle.getCallingUserId();
@@ -3334,7 +3333,7 @@
     private void grantUsingBackgroundNetworksPermissionForUid(
             final int uid, final String packageName) throws Exception {
         doReturn(buildPackageInfo(true /* hasSystemPermission */, uid)).when(mPackageManager)
-                .getPackageInfo(eq(packageName), eq(GET_PERMISSIONS | MATCH_ANY_USER));
+                .getPackageInfo(eq(packageName), eq(GET_PERMISSIONS));
         mService.mPermissionMonitor.onPackageAdded(packageName, uid);
     }
 
@@ -14666,4 +14665,11 @@
         mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
         mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
     }
+
+    @Test
+    public void testRequestRouteToHostAddress_PackageDoesNotBelongToCaller() {
+        assertThrows(SecurityException.class, () -> mService.requestRouteToHostAddress(
+                ConnectivityManager.TYPE_NONE, null /* hostAddress */, "com.not.package.owner",
+                null /* callingAttributionTag */));
+    }
 }
diff --git a/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
index 5bbbe40..45f3d3c 100644
--- a/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -60,6 +60,7 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
 import android.system.Os;
 import android.test.mock.MockContext;
 import android.util.ArraySet;
@@ -188,9 +189,15 @@
         }
     }
 
+    private IpSecService.Dependencies makeDependencies() throws RemoteException {
+        final IpSecService.Dependencies deps = mock(IpSecService.Dependencies.class);
+        when(deps.getNetdInstance(mTestContext)).thenReturn(mMockNetd);
+        return deps;
+    }
+
     INetd mMockNetd;
     PackageManager mMockPkgMgr;
-    IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
+    IpSecService.Dependencies mDeps;
     IpSecService mIpSecService;
     Network fakeNetwork = new Network(0xAB);
     int mUid = Os.getuid();
@@ -219,11 +226,8 @@
     public void setUp() throws Exception {
         mMockNetd = mock(INetd.class);
         mMockPkgMgr = mock(PackageManager.class);
-        mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
-        mIpSecService = new IpSecService(mTestContext, mMockIpSecSrvConfig);
-
-        // Injecting mock netd
-        when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd);
+        mDeps = makeDependencies();
+        mIpSecService = new IpSecService(mTestContext, mDeps);
 
         // PackageManager should always return true (feature flag tests in IpSecServiceTest)
         when(mMockPkgMgr.hasSystemFeature(anyString())).thenReturn(true);
diff --git a/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java b/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java
index 6957d51..5c7ca6f 100644
--- a/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java
+++ b/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java
@@ -57,14 +57,14 @@
 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
 public class IpSecServiceRefcountedResourceTest {
     Context mMockContext;
-    IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
+    IpSecService.Dependencies mMockDeps;
     IpSecService mIpSecService;
 
     @Before
     public void setUp() throws Exception {
         mMockContext = mock(Context.class);
-        mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
-        mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
+        mMockDeps = mock(IpSecService.Dependencies.class);
+        mIpSecService = new IpSecService(mMockContext, mMockDeps);
     }
 
     private void assertResourceState(
diff --git a/tests/unit/java/com/android/server/IpSecServiceTest.java b/tests/unit/java/com/android/server/IpSecServiceTest.java
index fabd6f1..7e6b157 100644
--- a/tests/unit/java/com/android/server/IpSecServiceTest.java
+++ b/tests/unit/java/com/android/server/IpSecServiceTest.java
@@ -46,6 +46,7 @@
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
+import android.os.RemoteException;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.StructStat;
@@ -122,24 +123,22 @@
 
     Context mMockContext;
     INetd mMockNetd;
-    IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
+    IpSecService.Dependencies mDeps;
     IpSecService mIpSecService;
 
     @Before
     public void setUp() throws Exception {
         mMockContext = mock(Context.class);
         mMockNetd = mock(INetd.class);
-        mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
-        mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
-
-        // Injecting mock netd
-        when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd);
+        mDeps = makeDependencies();
+        mIpSecService = new IpSecService(mMockContext, mDeps);
+        assertNotNull(mIpSecService);
     }
 
-    @Test
-    public void testIpSecServiceCreate() throws InterruptedException {
-        IpSecService ipSecSrv = IpSecService.create(mMockContext);
-        assertNotNull(ipSecSrv);
+    private IpSecService.Dependencies makeDependencies() throws RemoteException {
+        final IpSecService.Dependencies deps = mock(IpSecService.Dependencies.class);
+        when(deps.getNetdInstance(mMockContext)).thenReturn(mMockNetd);
+        return deps;
     }
 
     @Test
@@ -611,7 +610,7 @@
     public void testOpenUdpEncapSocketTagsSocket() throws Exception {
         IpSecService.UidFdTagger mockTagger = mock(IpSecService.UidFdTagger.class);
         IpSecService testIpSecService = new IpSecService(
-                mMockContext, mMockIpSecSrvConfig, mockTagger);
+                mMockContext, mDeps, mockTagger);
 
         IpSecUdpEncapResponse udpEncapResp =
                 testIpSecService.openUdpEncapsulationSocket(0, new Binder());
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index 4172553..6d1d765 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -92,7 +92,6 @@
     public TestRule compatChangeRule = new PlatformCompatChangeRule();
     @Mock Context mContext;
     @Mock ContentResolver mResolver;
-    @Mock NsdService.NsdSettings mSettings;
     NativeCallbackReceiver mDaemonCallback;
     @Spy DaemonConnection mDaemon = new DaemonConnection(mDaemonCallback);
     HandlerThread mThread;
@@ -129,7 +128,6 @@
     @Test
     @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
     public void testPreSClients() throws Exception {
-        when(mSettings.isEnabled()).thenReturn(true);
         NsdService service = makeService();
 
         // Pre S client connected, the daemon should be started.
@@ -160,7 +158,6 @@
     @Test
     @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
     public void testNoDaemonStartedWhenClientsConnect() throws Exception {
-        when(mSettings.isEnabled()).thenReturn(true);
         final NsdService service = makeService();
 
         // Creating an NsdManager will not cause any cmds executed, which means
@@ -197,7 +194,6 @@
     @Test
     @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
     public void testClientRequestsAreGCedAtDisconnection() throws Exception {
-        when(mSettings.isEnabled()).thenReturn(true);
         NsdService service = makeService();
 
         NsdManager client = connectClient(service);
@@ -242,8 +238,6 @@
     @Test
     @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
     public void testCleanupDelayNoRequestActive() throws Exception {
-        when(mSettings.isEnabled()).thenReturn(true);
-
         NsdService service = makeService();
         NsdManager client = connectClient(service);
 
@@ -277,8 +271,7 @@
             mDaemonCallback = callback;
             return mDaemon;
         };
-        final NsdService service = new NsdService(mContext, mSettings,
-                mHandler, supplier, CLEANUP_DELAY_MS) {
+        final NsdService service = new NsdService(mContext, mHandler, supplier, CLEANUP_DELAY_MS) {
             @Override
             public INsdServiceConnector connect(INsdManagerCallback baseCb) {
                 // Wrap the callback in a transparent mock, to mock asBinder returning a
diff --git a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
index ecda338..99ef80b 100644
--- a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -60,6 +60,7 @@
 import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -118,10 +119,13 @@
     private static final int SYSTEM_APPID1 = 1100;
     private static final int SYSTEM_APPID2 = 1108;
     private static final int VPN_APPID = 10002;
-    private static final int MOCK_UID1 = MOCK_USER1.getUid(MOCK_APPID1);
-    private static final int MOCK_UID2 = MOCK_USER1.getUid(MOCK_APPID2);
-    private static final int SYSTEM_APP_UID1 = MOCK_USER1.getUid(SYSTEM_APPID1);
+    private static final int MOCK_UID11 = MOCK_USER1.getUid(MOCK_APPID1);
+    private static final int MOCK_UID12 = MOCK_USER1.getUid(MOCK_APPID2);
+    private static final int SYSTEM_APP_UID11 = MOCK_USER1.getUid(SYSTEM_APPID1);
     private static final int VPN_UID = MOCK_USER1.getUid(VPN_APPID);
+    private static final int MOCK_UID21 = MOCK_USER2.getUid(MOCK_APPID1);
+    private static final int MOCK_UID22 = MOCK_USER2.getUid(MOCK_APPID2);
+    private static final int SYSTEM_APP_UID21 = MOCK_USER2.getUid(SYSTEM_APPID1);
     private static final String REAL_SYSTEM_PACKAGE_NAME = "android";
     private static final String MOCK_PACKAGE1 = "appName1";
     private static final String MOCK_PACKAGE2 = "appName2";
@@ -161,9 +165,13 @@
             doCallRealMethod().when(mContext).getSystemService(SystemConfigManager.class);
         }
         when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]);
-        final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mContext));
-        doReturn(UserHandle.ALL).when(asUserCtx).getUser();
-        when(mContext.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx);
+        doAnswer(invocation -> {
+            final Object[] args = invocation.getArguments();
+            final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mContext));
+            final UserHandle user = (UserHandle) args[0];
+            doReturn(user).when(asUserCtx).getUser();
+            return asUserCtx;
+        }).when(mContext).createContextAsUser(any(), anyInt());
         when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of());
         // Set DEVICE_INITIAL_SDK_INT to Q that SYSTEM_UID won't have restricted network permission
         // by default.
@@ -172,7 +180,7 @@
         mPermissionMonitor = new PermissionMonitor(mContext, mNetdService, mDeps);
         mNetdMonitor = new NetdMonitor(mNetdService);
 
-        doReturn(List.of()).when(mPackageManager).getInstalledPackages(anyInt());
+        doReturn(List.of()).when(mPackageManager).getInstalledPackagesAsUser(anyInt(), anyInt());
     }
 
     private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion,
@@ -330,26 +338,26 @@
     @Test
     public void testHasRestrictedNetworkPermission() {
         assertFalse(hasRestrictedNetworkPermission(
-                PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID1));
+                PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID11));
         assertFalse(hasRestrictedNetworkPermission(
-                PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE));
+                PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE));
         assertTrue(hasRestrictedNetworkPermission(
-                PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID1, NETWORK_STACK));
+                PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, NETWORK_STACK));
         assertFalse(hasRestrictedNetworkPermission(
-                PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID1, CONNECTIVITY_INTERNAL));
+                PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, CONNECTIVITY_INTERNAL));
         assertTrue(hasRestrictedNetworkPermission(
-                PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID1,
+                PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID11,
                 CONNECTIVITY_USE_RESTRICTED_NETWORKS));
         assertFalse(hasRestrictedNetworkPermission(
-                PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID1, CHANGE_WIFI_STATE));
+                PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, CHANGE_WIFI_STATE));
         assertTrue(hasRestrictedNetworkPermission(
-                PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID1,
+                PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID11,
                 PERMISSION_MAINLINE_NETWORK_STACK));
 
         assertFalse(hasRestrictedNetworkPermission(
-                PARTITION_SYSTEM, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1));
+                PARTITION_SYSTEM, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11));
         assertFalse(hasRestrictedNetworkPermission(
-                PARTITION_SYSTEM, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1, CONNECTIVITY_INTERNAL));
+                PARTITION_SYSTEM, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11, CONNECTIVITY_INTERNAL));
     }
 
     @Test
@@ -376,43 +384,43 @@
     @Test
     public void testHasRestrictedNetworkPermissionVendorApp() {
         assertTrue(hasRestrictedNetworkPermission(
-                PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID1));
+                PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID11));
         assertTrue(hasRestrictedNetworkPermission(
-                PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE));
+                PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE));
         assertTrue(hasRestrictedNetworkPermission(
-                PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID1, NETWORK_STACK));
+                PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, NETWORK_STACK));
         assertTrue(hasRestrictedNetworkPermission(
-                PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID1, CONNECTIVITY_INTERNAL));
+                PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, CONNECTIVITY_INTERNAL));
         assertTrue(hasRestrictedNetworkPermission(
-                PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID1,
+                PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID11,
                 CONNECTIVITY_USE_RESTRICTED_NETWORKS));
         assertTrue(hasRestrictedNetworkPermission(
-                PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID1, CHANGE_WIFI_STATE));
+                PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, CHANGE_WIFI_STATE));
 
         assertFalse(hasRestrictedNetworkPermission(
-                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1));
+                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11));
         assertFalse(hasRestrictedNetworkPermission(
-                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1, CONNECTIVITY_INTERNAL));
+                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11, CONNECTIVITY_INTERNAL));
         assertFalse(hasRestrictedNetworkPermission(
-                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE));
+                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE));
     }
 
     @Test
     public void testHasRestrictedNetworkPermissionUidAllowedOnRestrictedNetworks() {
-        mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(Set.of(MOCK_UID1));
+        mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(Set.of(MOCK_UID11));
         assertTrue(hasRestrictedNetworkPermission(
-                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1));
+                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11));
         assertTrue(hasRestrictedNetworkPermission(
-                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE));
+                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE));
         assertTrue(hasRestrictedNetworkPermission(
-                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1, CONNECTIVITY_INTERNAL));
+                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11, CONNECTIVITY_INTERNAL));
 
         assertFalse(hasRestrictedNetworkPermission(
-                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID2));
+                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID12));
         assertFalse(hasRestrictedNetworkPermission(
-                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID2, CHANGE_NETWORK_STATE));
+                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID12, CHANGE_NETWORK_STATE));
         assertFalse(hasRestrictedNetworkPermission(
-                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID2, CONNECTIVITY_INTERNAL));
+                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID12, CONNECTIVITY_INTERNAL));
 
     }
 
@@ -429,27 +437,27 @@
         doReturn(VERSION_P).when(mDeps).getDeviceFirstSdkInt();
         assertTrue(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
         assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, SYSTEM_UID));
-        assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID1));
-        assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID1));
+        assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID11));
+        assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID11));
         assertTrue(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
         assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, SYSTEM_UID));
-        assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
-        assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
+        assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID11));
+        assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID11));
 
         doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt();
         assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
         assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, SYSTEM_UID));
-        assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID1));
-        assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID1));
+        assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID11));
+        assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID11));
         assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
         assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, SYSTEM_UID));
-        assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
-        assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
+        assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID11));
+        assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID11));
 
         assertFalse(wouldBeCarryoverPackage(PARTITION_OEM, VERSION_Q, SYSTEM_UID));
         assertFalse(wouldBeCarryoverPackage(PARTITION_PRODUCT, VERSION_Q, SYSTEM_UID));
-        assertFalse(wouldBeCarryoverPackage(PARTITION_OEM, VERSION_Q, MOCK_UID1));
-        assertFalse(wouldBeCarryoverPackage(PARTITION_PRODUCT, VERSION_Q, MOCK_UID1));
+        assertFalse(wouldBeCarryoverPackage(PARTITION_OEM, VERSION_Q, MOCK_UID11));
+        assertFalse(wouldBeCarryoverPackage(PARTITION_PRODUCT, VERSION_Q, MOCK_UID11));
     }
 
     private boolean wouldBeUidAllowedOnRestrictedNetworks(int uid) {
@@ -461,20 +469,20 @@
     @Test
     public void testIsAppAllowedOnRestrictedNetworks() {
         mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(Set.of());
-        assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID1));
-        assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID2));
+        assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID11));
+        assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID12));
 
-        mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(Set.of(MOCK_UID1));
-        assertTrue(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID1));
-        assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID2));
+        mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(Set.of(MOCK_UID11));
+        assertTrue(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID11));
+        assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID12));
 
-        mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(Set.of(MOCK_UID2));
-        assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID1));
-        assertTrue(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID2));
+        mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(Set.of(MOCK_UID12));
+        assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID11));
+        assertTrue(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID12));
 
         mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(Set.of(123));
-        assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID1));
-        assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID2));
+        assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID11));
+        assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID12));
     }
 
     private void assertBackgroundPermission(boolean hasPermission, String name, int uid,
@@ -491,16 +499,16 @@
         assertBackgroundPermission(true, SYSTEM_PACKAGE1, SYSTEM_UID, CHANGE_NETWORK_STATE);
         assertBackgroundPermission(true, SYSTEM_PACKAGE1, SYSTEM_UID, NETWORK_STACK);
 
-        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID1));
-        assertBackgroundPermission(false, MOCK_PACKAGE1, MOCK_UID1);
-        assertBackgroundPermission(true, MOCK_PACKAGE1, MOCK_UID1,
+        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID11));
+        assertBackgroundPermission(false, MOCK_PACKAGE1, MOCK_UID11);
+        assertBackgroundPermission(true, MOCK_PACKAGE1, MOCK_UID11,
                 CONNECTIVITY_USE_RESTRICTED_NETWORKS);
 
-        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID2));
-        assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID2);
-        assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID2,
+        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID12));
+        assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID12);
+        assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID12,
                 CONNECTIVITY_INTERNAL);
-        assertBackgroundPermission(true, MOCK_PACKAGE2, MOCK_UID2, NETWORK_STACK);
+        assertBackgroundPermission(true, MOCK_PACKAGE2, MOCK_UID12, NETWORK_STACK);
     }
 
     private class NetdMonitor {
@@ -589,13 +597,13 @@
 
     @Test
     public void testUserAndPackageAddRemove() throws Exception {
-        // MOCK_UID1: MOCK_PACKAGE1 only has network permission.
-        // SYSTEM_APP_UID1: SYSTEM_PACKAGE1 has system permission.
-        // SYSTEM_APP_UID1: SYSTEM_PACKAGE2 only has network permission.
-        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE);
-        buildAndMockPackageInfoWithPermissions(SYSTEM_PACKAGE1, SYSTEM_APP_UID1,
+        // MOCK_UID11: MOCK_PACKAGE1 only has network permission.
+        // SYSTEM_APP_UID11: SYSTEM_PACKAGE1 has system permission.
+        // SYSTEM_APP_UID11: SYSTEM_PACKAGE2 only has network permission.
+        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE);
+        buildAndMockPackageInfoWithPermissions(SYSTEM_PACKAGE1, SYSTEM_APP_UID11,
                 CONNECTIVITY_USE_RESTRICTED_NETWORKS);
-        buildAndMockPackageInfoWithPermissions(SYSTEM_PACKAGE2, SYSTEM_APP_UID1,
+        buildAndMockPackageInfoWithPermissions(SYSTEM_PACKAGE2, SYSTEM_APP_UID11,
                 CHANGE_NETWORK_STATE);
 
         // Add user MOCK_USER1.
@@ -610,16 +618,19 @@
         mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
                 SYSTEM_APPID1);
 
+        final List<PackageInfo> pkgs = List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_APP_UID21,
+                        CONNECTIVITY_USE_RESTRICTED_NETWORKS),
+                buildPackageInfo(SYSTEM_PACKAGE2, SYSTEM_APP_UID21, CHANGE_NETWORK_STATE));
+        doReturn(pkgs).when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS),
+                eq(MOCK_USER_ID2));
         // Add user MOCK_USER2.
         mPermissionMonitor.onUserAdded(MOCK_USER2);
         mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
                 SYSTEM_APPID1);
 
         // Remove SYSTEM_PACKAGE2, expect keep system permission.
-        when(mPackageManager.getPackagesForUid(SYSTEM_APP_UID1))
-                .thenReturn(new String[]{SYSTEM_PACKAGE1});
-        when(mPackageManager.getPackagesForUid(MOCK_USER2.getUid(SYSTEM_APPID1)))
-                .thenReturn(new String[]{SYSTEM_PACKAGE1});
+        doReturn(new String[]{SYSTEM_PACKAGE1}).when(mPackageManager)
+                .getPackagesForUid(intThat(uid -> UserHandle.getAppId(uid) == SYSTEM_APPID1));
         removePackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2},
                 SYSTEM_PACKAGE2, SYSTEM_APPID1);
         mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
@@ -631,6 +642,8 @@
         mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
                 SYSTEM_APPID1);
 
+        // Add MOCK_PACKAGE1
+        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID21, CHANGE_NETWORK_STATE);
         addPackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_APPID1);
         mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
                 SYSTEM_APPID1);
@@ -638,9 +651,8 @@
                 MOCK_APPID1);
 
         // Remove MOCK_PACKAGE1, expect no permission left for all user.
-        when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{});
-        when(mPackageManager.getPackagesForUid(MOCK_USER2.getUid(MOCK_APPID1)))
-                .thenReturn(new String[]{});
+        doReturn(new String[]{}).when(mPackageManager)
+                .getPackagesForUid(intThat(uid -> UserHandle.getAppId(uid) == MOCK_APPID1));
         removePackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_APPID1);
         mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_APPID1);
 
@@ -674,68 +686,71 @@
 
     @Test
     public void testUidFilteringDuringVpnConnectDisconnectAndUidUpdates() throws Exception {
-        when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
-                List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_APP_UID1, CHANGE_NETWORK_STATE,
-                                CONNECTIVITY_USE_RESTRICTED_NETWORKS),
-                        buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1),
-                        buildPackageInfo(MOCK_PACKAGE2, MOCK_UID2),
-                        buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID)));
-        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1);
+        doReturn(List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_APP_UID11, CHANGE_NETWORK_STATE,
+                        CONNECTIVITY_USE_RESTRICTED_NETWORKS),
+                buildPackageInfo(MOCK_PACKAGE1, MOCK_UID11),
+                buildPackageInfo(MOCK_PACKAGE2, MOCK_UID12),
+                buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID)))
+                .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
+        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11);
         mPermissionMonitor.startMonitoring();
-        // Every app on user 0 except MOCK_UID2 are under VPN.
+        // Every app on user 0 except MOCK_UID12 are under VPN.
         final Set<UidRange> vpnRange1 = Set.of(
-                new UidRange(0, MOCK_UID2 - 1),
-                new UidRange(MOCK_UID2 + 1, UserHandle.PER_USER_RANGE - 1));
-        final Set<UidRange> vpnRange2 = Set.of(new UidRange(MOCK_UID2, MOCK_UID2));
+                new UidRange(0, MOCK_UID12 - 1),
+                new UidRange(MOCK_UID12 + 1, UserHandle.PER_USER_RANGE - 1));
+        final Set<UidRange> vpnRange2 = Set.of(new UidRange(MOCK_UID12, MOCK_UID12));
 
-        // When VPN is connected, expect a rule to be set up for user app MOCK_UID1
+        // When VPN is connected, expect a rule to be set up for user app MOCK_UID11
         mPermissionMonitor.onVpnUidRangesAdded("tun0", vpnRange1, VPN_UID);
-        verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID1}));
+        verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID11}));
 
         reset(mNetdService);
 
-        // When MOCK_UID1 package is uninstalled and reinstalled, expect Netd to be updated
-        mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
-        verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[]{MOCK_UID1}));
-        mPermissionMonitor.onPackageAdded(MOCK_PACKAGE1, MOCK_UID1);
-        verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID1}));
+        // When MOCK_UID11 package is uninstalled and reinstalled, expect Netd to be updated
+        mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
+        verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[]{MOCK_UID11}));
+        mPermissionMonitor.onPackageAdded(MOCK_PACKAGE1, MOCK_UID11);
+        verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID11}));
 
         reset(mNetdService);
 
         // During VPN uid update (vpnRange1 -> vpnRange2), ConnectivityService first deletes the
         // old UID rules then adds the new ones. Expect netd to be updated
         mPermissionMonitor.onVpnUidRangesRemoved("tun0", vpnRange1, VPN_UID);
-        verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID1}));
+        verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID11}));
         mPermissionMonitor.onVpnUidRangesAdded("tun0", vpnRange2, VPN_UID);
-        verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID2}));
+        verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID12}));
 
         reset(mNetdService);
 
         // When VPN is disconnected, expect rules to be torn down
         mPermissionMonitor.onVpnUidRangesRemoved("tun0", vpnRange2, VPN_UID);
-        verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID2}));
+        verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID12}));
         assertNull(mPermissionMonitor.getVpnUidRanges("tun0"));
     }
 
     @Test
     public void testUidFilteringDuringPackageInstallAndUninstall() throws Exception {
-        when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
-                List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_APP_UID1, CHANGE_NETWORK_STATE,
-                                NETWORK_STACK, CONNECTIVITY_USE_RESTRICTED_NETWORKS),
-                        buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID)));
-        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1);
+        doReturn(List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_APP_UID11, CHANGE_NETWORK_STATE,
+                        NETWORK_STACK, CONNECTIVITY_USE_RESTRICTED_NETWORKS),
+                buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID)))
+                .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
+        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11);
 
         mPermissionMonitor.startMonitoring();
-        final Set<UidRange> vpnRange = Set.of(UidRange.createForUser(MOCK_USER1));
+        final Set<UidRange> vpnRange = Set.of(UidRange.createForUser(MOCK_USER1),
+                UidRange.createForUser(MOCK_USER2));
         mPermissionMonitor.onVpnUidRangesAdded("tun0", vpnRange, VPN_UID);
 
         // Newly-installed package should have uid rules added
-        mPermissionMonitor.onPackageAdded(MOCK_PACKAGE1, MOCK_UID1);
-        verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID1}));
+        addPackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_APPID1);
+        verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID11}));
+        verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID21}));
 
         // Removed package should have its uid rules removed
-        mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
-        verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID1}));
+        mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
+        verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[]{MOCK_UID11}));
+        verify(mNetdService, never()).firewallRemoveUidInterfaceRules(aryEq(new int[]{MOCK_UID21}));
     }
 
 
@@ -767,7 +782,7 @@
         netdPermissionsAppIds.put(SYSTEM_APPID2, PERMISSION_UPDATE_DEVICE_STATS);
 
         // Send the permission information to netd, expect permission updated.
-        mPermissionMonitor.sendPackagePermissionsToNetd(netdPermissionsAppIds);
+        mPermissionMonitor.sendAppIdsTrafficPermission(netdPermissionsAppIds);
 
         mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
         mNetdMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_APPID2);
@@ -790,69 +805,69 @@
 
     @Test
     public void testPackageInstall() throws Exception {
-        addPackage(MOCK_PACKAGE1, MOCK_UID1, INTERNET, UPDATE_DEVICE_STATS);
+        addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET, UPDATE_DEVICE_STATS);
         mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
 
-        addPackage(MOCK_PACKAGE2, MOCK_UID2, INTERNET);
+        addPackage(MOCK_PACKAGE2, MOCK_UID12, INTERNET);
         mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID2);
     }
 
     @Test
     public void testPackageInstallSharedUid() throws Exception {
-        addPackage(MOCK_PACKAGE1, MOCK_UID1, INTERNET, UPDATE_DEVICE_STATS);
+        addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET, UPDATE_DEVICE_STATS);
         mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
 
         // Install another package with the same uid and no permissions should not cause the app id
         // to lose permissions.
-        addPackage(MOCK_PACKAGE2, MOCK_UID1);
+        addPackage(MOCK_PACKAGE2, MOCK_UID11);
         mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
     }
 
     @Test
     public void testPackageUninstallBasic() throws Exception {
-        addPackage(MOCK_PACKAGE1, MOCK_UID1, INTERNET, UPDATE_DEVICE_STATS);
+        addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET, UPDATE_DEVICE_STATS);
         mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
 
-        when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{});
-        mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
+        when(mPackageManager.getPackagesForUid(MOCK_UID11)).thenReturn(new String[]{});
+        mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
         mNetdMonitor.expectTrafficPerm(PERMISSION_UNINSTALLED, MOCK_APPID1);
     }
 
     @Test
     public void testPackageRemoveThenAdd() throws Exception {
-        addPackage(MOCK_PACKAGE1, MOCK_UID1, INTERNET, UPDATE_DEVICE_STATS);
+        addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET, UPDATE_DEVICE_STATS);
         mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
 
-        when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{});
-        mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
+        when(mPackageManager.getPackagesForUid(MOCK_UID11)).thenReturn(new String[]{});
+        mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
         mNetdMonitor.expectTrafficPerm(PERMISSION_UNINSTALLED, MOCK_APPID1);
 
-        addPackage(MOCK_PACKAGE1, MOCK_UID1, INTERNET);
+        addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET);
         mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
     }
 
     @Test
     public void testPackageUpdate() throws Exception {
-        addPackage(MOCK_PACKAGE1, MOCK_UID1);
+        addPackage(MOCK_PACKAGE1, MOCK_UID11);
         mNetdMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_APPID1);
 
-        addPackage(MOCK_PACKAGE1, MOCK_UID1, INTERNET);
+        addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET);
         mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
     }
 
     @Test
     public void testPackageUninstallWithMultiplePackages() throws Exception {
-        addPackage(MOCK_PACKAGE1, MOCK_UID1, INTERNET, UPDATE_DEVICE_STATS);
+        addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET, UPDATE_DEVICE_STATS);
         mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
 
         // Install another package with the same uid but different permissions.
-        addPackage(MOCK_PACKAGE2, MOCK_UID1, INTERNET);
-        mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_UID1);
+        addPackage(MOCK_PACKAGE2, MOCK_UID11, INTERNET);
+        mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_UID11);
 
         // Uninstall MOCK_PACKAGE1 and expect only INTERNET permission left.
-        when(mPackageManager.getPackagesForUid(eq(MOCK_UID1)))
+        when(mPackageManager.getPackagesForUid(eq(MOCK_UID11)))
                 .thenReturn(new String[]{MOCK_PACKAGE2});
-        mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
+        mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
         mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
     }
 
@@ -871,9 +886,9 @@
     @Test
     public void testUpdateUidPermissionsFromSystemConfig() throws Exception {
         when(mSystemConfigManager.getSystemPermissionUids(eq(INTERNET)))
-                .thenReturn(new int[]{ MOCK_UID1, MOCK_UID2 });
+                .thenReturn(new int[]{ MOCK_UID11, MOCK_UID12 });
         when(mSystemConfigManager.getSystemPermissionUids(eq(UPDATE_DEVICE_STATS)))
-                .thenReturn(new int[]{ MOCK_UID2 });
+                .thenReturn(new int[]{ MOCK_UID12 });
 
         mPermissionMonitor.startMonitoring();
         mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
@@ -904,17 +919,17 @@
         // Verify receiving PACKAGE_ADDED intent.
         final Intent addedIntent = new Intent(Intent.ACTION_PACKAGE_ADDED,
                 Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */));
-        addedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1);
-        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1, INTERNET,
+        addedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID11);
+        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11, INTERNET,
                 UPDATE_DEVICE_STATS);
         receiver.onReceive(mContext, addedIntent);
         mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
 
         // Verify receiving PACKAGE_REMOVED intent.
-        when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{});
+        when(mPackageManager.getPackagesForUid(MOCK_UID11)).thenReturn(new String[]{});
         final Intent removedIntent = new Intent(Intent.ACTION_PACKAGE_REMOVED,
                 Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */));
-        removedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1);
+        removedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID11);
         receiver.onReceive(mContext, removedIntent);
         mNetdMonitor.expectTrafficPerm(PERMISSION_UNINSTALLED, MOCK_APPID1);
     }
@@ -934,20 +949,20 @@
                 Settings.Global.getUriFor(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS));
 
         // Prepare PackageInfo for MOCK_PACKAGE1 and MOCK_PACKAGE2
-        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1);
-        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID2);
+        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11);
+        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID12);
 
-        // MOCK_UID1 is listed in setting that allow to use restricted networks, MOCK_UID1
+        // MOCK_UID11 is listed in setting that allow to use restricted networks, MOCK_UID11
         // should have SYSTEM permission.
-        when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID1));
+        when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID11));
         contentObserver.onChange(true /* selfChange */);
         mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
                 MOCK_APPID1);
         mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID2);
 
-        // MOCK_UID2 is listed in setting that allow to use restricted networks, MOCK_UID2
-        // should have SYSTEM permission but MOCK_UID1 should revoke permission.
-        when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID2));
+        // MOCK_UID12 is listed in setting that allow to use restricted networks, MOCK_UID12
+        // should have SYSTEM permission but MOCK_UID11 should revoke permission.
+        when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID12));
         contentObserver.onChange(true /* selfChange */);
         mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
                 MOCK_APPID2);
@@ -965,29 +980,29 @@
         final ContentObserver contentObserver = expectRegisterContentObserver(
                 Settings.Global.getUriFor(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS));
 
-        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE);
-        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID1);
+        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE);
+        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID11);
 
-        // MOCK_PACKAGE1 have CHANGE_NETWORK_STATE, MOCK_UID1 should have NETWORK permission.
+        // MOCK_PACKAGE1 have CHANGE_NETWORK_STATE, MOCK_UID11 should have NETWORK permission.
         addPackageForUsers(new UserHandle[]{MOCK_USER1}, MOCK_PACKAGE1, MOCK_APPID1);
         mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1},
                 MOCK_APPID1);
 
-        // MOCK_UID1 is listed in setting that allow to use restricted networks, MOCK_UID1
+        // MOCK_UID11 is listed in setting that allow to use restricted networks, MOCK_UID11
         // should upgrade to SYSTEM permission.
-        when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID1));
+        when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID11));
         contentObserver.onChange(true /* selfChange */);
         mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
                 MOCK_APPID1);
 
-        // No app lists in setting, MOCK_UID1 should downgrade to NETWORK permission.
+        // No app lists in setting, MOCK_UID11 should downgrade to NETWORK permission.
         when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of());
         contentObserver.onChange(true /* selfChange */);
         mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1},
                 MOCK_APPID1);
 
-        // MOCK_PACKAGE1 removed, should revoke permission from MOCK_UID1.
-        when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{MOCK_PACKAGE2});
+        // MOCK_PACKAGE1 removed, should revoke permission from MOCK_UID11.
+        when(mPackageManager.getPackagesForUid(MOCK_UID11)).thenReturn(new String[]{MOCK_PACKAGE2});
         removePackageForUsers(new UserHandle[]{MOCK_USER1}, MOCK_PACKAGE1, MOCK_APPID1);
         mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID1);
     }
@@ -998,33 +1013,39 @@
         final ContentObserver contentObserver = expectRegisterContentObserver(
                 Settings.Global.getUriFor(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS));
 
-        // Prepare PackageInfo for MOCK_PACKAGE1 and MOCK_PACKAGE2.
-        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1);
-        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID2);
+        // Prepare PackageInfo for MOCK_APPID1 and MOCK_APPID2 in MOCK_USER1.
+        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11);
+        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID12);
 
-        // MOCK_UID1 is listed in setting that allow to use restricted networks, MOCK_UID1
-        // should have SYSTEM permission and MOCK_UID2 has no permissions.
-        when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID1));
+        // MOCK_UID11 is listed in setting that allow to use restricted networks, MOCK_UID11 should
+        // have SYSTEM permission and MOCK_UID12 has no permissions.
+        when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID11));
         contentObserver.onChange(true /* selfChange */);
         mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
                 MOCK_APPID1);
         mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID2);
 
         // Add user MOCK_USER2.
+        final List<PackageInfo> pkgs = List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID21));
+        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID22);
+        doReturn(pkgs).when(mPackageManager)
+                .getInstalledPackagesAsUser(eq(GET_PERMISSIONS), eq(MOCK_USER_ID2));
         mPermissionMonitor.onUserAdded(MOCK_USER2);
-        // MOCK_APPID1 in both users should all have SYSTEM permission and MOCK_APPID2 has no
-        // permissions in either user.
-        mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
+        // MOCK_APPID1 in MOCK_USER1 should have SYSTEM permission but in MOCK_USER2 should have no
+        // permissions. And MOCK_APPID2 has no permissions in either users.
+        mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
                 MOCK_APPID1);
+        mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER2}, MOCK_APPID1);
         mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_APPID2);
 
-        // MOCK_UID2 is listed in setting that allow to use restricted networks, MOCK_APPID2
-        // in both users should have SYSTEM permission and MOCK_APPID1 has no permissions in either
-        // user.
-        when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID2));
+        // MOCK_UID22 is listed in setting that allow to use restricted networks,
+        // MOCK_APPID2 in MOCK_USER2 should have SYSTEM permission but in MOCK_USER1 should have no
+        // permissions. And MOCK_APPID1 has no permissions in either users.
+        doReturn(Set.of(MOCK_UID22)).when(mDeps).getUidsAllowedOnRestrictedNetworks(any());
         contentObserver.onChange(true /* selfChange */);
-        mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
+        mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER2},
                 MOCK_APPID2);
+        mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID2);
         mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_APPID1);
 
         // Remove user MOCK_USER1
@@ -1044,9 +1065,9 @@
     public void testOnExternalApplicationsAvailable() throws Exception {
         // Initial the permission state. MOCK_PACKAGE1 and MOCK_PACKAGE2 are installed on external
         // and have different uids. There has no permission for both uids.
-        when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
-                List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1),
-                        buildPackageInfo(MOCK_PACKAGE2, MOCK_UID2)));
+        doReturn(List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID11),
+                buildPackageInfo(MOCK_PACKAGE2, MOCK_UID12)))
+                .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
         mPermissionMonitor.startMonitoring();
         mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID1, MOCK_APPID2);
         mNetdMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_APPID1, MOCK_APPID2);
@@ -1057,9 +1078,9 @@
         final Intent externalIntent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
         externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST,
                 new String[] { MOCK_PACKAGE1 , MOCK_PACKAGE2});
-        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1,
+        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11,
                 CONNECTIVITY_USE_RESTRICTED_NETWORKS, INTERNET);
-        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID2, CHANGE_NETWORK_STATE,
+        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID12, CHANGE_NETWORK_STATE,
                 UPDATE_DEVICE_STATS);
         receiver.onReceive(mContext, externalIntent);
         mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
@@ -1079,9 +1100,9 @@
 
         // Initial the permission state. MOCK_PACKAGE1 and MOCK_PACKAGE2 are installed on external
         // and have different uids. There has no permission for both uids.
-        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1,
+        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11,
                 CONNECTIVITY_USE_RESTRICTED_NETWORKS, INTERNET);
-        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID2, CHANGE_NETWORK_STATE,
+        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID12, CHANGE_NETWORK_STATE,
                 UPDATE_DEVICE_STATS);
 
         // Verify receiving EXTERNAL_APPLICATIONS_AVAILABLE intent and update permission to netd.
@@ -1101,10 +1122,10 @@
     public void testOnExternalApplicationsAvailableWithSharedUid()
             throws Exception {
         // Initial the permission state. MOCK_PACKAGE1 and MOCK_PACKAGE2 are installed on external
-        // storage and shared on MOCK_UID1. There has no permission for MOCK_UID1.
-        when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
-                List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1),
-                        buildPackageInfo(MOCK_PACKAGE2, MOCK_UID1)));
+        // storage and shared on MOCK_UID11. There has no permission for MOCK_UID11.
+        doReturn(List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID11),
+                buildPackageInfo(MOCK_PACKAGE2, MOCK_UID11)))
+                .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
         mPermissionMonitor.startMonitoring();
         mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID1);
         mNetdMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_APPID1);
@@ -1114,8 +1135,8 @@
         // Verify receiving EXTERNAL_APPLICATIONS_AVAILABLE intent and update permission to netd.
         final Intent externalIntent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
         externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST, new String[] {MOCK_PACKAGE1});
-        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE);
-        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID1, UPDATE_DEVICE_STATS);
+        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE);
+        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID11, UPDATE_DEVICE_STATS);
         receiver.onReceive(mContext, externalIntent);
         mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1},
                 MOCK_APPID1);
@@ -1126,12 +1147,11 @@
     public void testOnExternalApplicationsAvailableWithSharedUid_DifferentStorage()
             throws Exception {
         // Initial the permission state. MOCK_PACKAGE1 is installed on external storage and
-        // MOCK_PACKAGE2 is installed on device. These two packages are shared on MOCK_UID1.
-        // MOCK_UID1 has NETWORK and INTERNET permissions.
-        when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
-                List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1),
-                        buildPackageInfo(MOCK_PACKAGE2, MOCK_UID1, CHANGE_NETWORK_STATE,
-                                INTERNET)));
+        // MOCK_PACKAGE2 is installed on device. These two packages are shared on MOCK_UID11.
+        // MOCK_UID11 has NETWORK and INTERNET permissions.
+        doReturn(List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID11),
+                buildPackageInfo(MOCK_PACKAGE2, MOCK_UID11, CHANGE_NETWORK_STATE, INTERNET)))
+                .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
         mPermissionMonitor.startMonitoring();
         mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1},
                 MOCK_APPID1);
@@ -1142,9 +1162,9 @@
         // Verify receiving EXTERNAL_APPLICATIONS_AVAILABLE intent and update permission to netd.
         final Intent externalIntent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
         externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST, new String[] {MOCK_PACKAGE1});
-        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1,
+        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11,
                 CONNECTIVITY_USE_RESTRICTED_NETWORKS, UPDATE_DEVICE_STATS);
-        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID1, CHANGE_NETWORK_STATE,
+        buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID11, CHANGE_NETWORK_STATE,
                 INTERNET);
         receiver.onReceive(mContext, externalIntent);
         mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
index e35104e..416549c 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
@@ -37,7 +37,9 @@
 import android.app.usage.NetworkStatsManager;
 import android.net.DataUsageRequest;
 import android.net.NetworkIdentity;
+import android.net.NetworkIdentitySet;
 import android.net.NetworkStats;
+import android.net.NetworkStatsAccess;
 import android.net.NetworkTemplate;
 import android.os.Build;
 import android.os.ConditionVariable;
@@ -72,7 +74,7 @@
  */
 @RunWith(DevSdkIgnoreRunner.class)
 @SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S)
 public class NetworkStatsObserversTest {
     private static final String TEST_IFACE = "test0";
     private static final String TEST_IFACE2 = "test1";
diff --git a/tests/unit/jni/Android.bp b/tests/unit/jni/Android.bp
index 1c1ba9e..fe971e7 100644
--- a/tests/unit/jni/Android.bp
+++ b/tests/unit/jni/Android.bp
@@ -13,6 +13,8 @@
         "-Wthread-safety",
     ],
 
+    header_libs: ["bpf_connectivity_headers"],
+
     srcs: [
         ":lib_networkStatsFactory_native",
         "test_onload.cpp",