Merge changes Ia930b3d3,If614da81,I975a9439

* changes:
  [NS04.8] Address comments from NS04 and NS04.5
  [NS04.7] Reinstate a necessary change
  [NS04.5] Have NetworkOffer embark a provider ID
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index 79c2b9d..5a08b23 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -146,7 +146,10 @@
     manifest: "AndroidManifest.xml",
     use_embedded_native_libs: true,
     // The permission configuration *must* be included to ensure security of the device
-    required: ["NetworkPermissionConfig"],
+    required: [
+        "NetworkPermissionConfig",
+        "privapp_whitelist_com.android.networkstack.tethering",
+    ],
     apex_available: ["com.android.tethering"],
     min_sdk_version: "30",
 }
diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp
index e47d802..4bc8f93 100644
--- a/Tethering/common/TetheringLib/Android.bp
+++ b/Tethering/common/TetheringLib/Android.bp
@@ -56,6 +56,8 @@
         "src/android/net/TetheringConfigurationParcel.aidl",
         "src/android/net/TetheringRequestParcel.aidl",
         "src/android/net/TetherStatesParcel.aidl",
+        "src/android/net/TetheringInterface.aidl",
+        "src/android/net/TetheringInterface.java",
     ],
     path: "src"
 }
diff --git a/Tethering/common/TetheringLib/api/module-lib-current.txt b/Tethering/common/TetheringLib/api/module-lib-current.txt
index 6ddb122..0566040 100644
--- a/Tethering/common/TetheringLib/api/module-lib-current.txt
+++ b/Tethering/common/TetheringLib/api/module-lib-current.txt
@@ -28,13 +28,13 @@
   }
 
   public static interface TetheringManager.TetheringEventCallback {
-    method public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
+    method @Deprecated public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
   }
 
-  public static class TetheringManager.TetheringInterfaceRegexps {
-    method @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
-    method @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
-    method @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
+  @Deprecated public static class TetheringManager.TetheringInterfaceRegexps {
+    method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
+    method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
+    method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
   }
 
 }
diff --git a/Tethering/common/TetheringLib/api/system-current.txt b/Tethering/common/TetheringLib/api/system-current.txt
index 105bab1..844ff64 100644
--- a/Tethering/common/TetheringLib/api/system-current.txt
+++ b/Tethering/common/TetheringLib/api/system-current.txt
@@ -19,6 +19,15 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
   }
 
+  public final class TetheringInterface implements android.os.Parcelable {
+    ctor public TetheringInterface(int, @NonNull String);
+    method public int describeContents();
+    method @NonNull public String getInterface();
+    method public int getType();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheringInterface> CREATOR;
+  }
+
   public class TetheringManager {
     method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
@@ -26,7 +35,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
-    field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
+    field @Deprecated public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
     field public static final int CONNECTIVITY_SCOPE_GLOBAL = 1; // 0x1
     field public static final int CONNECTIVITY_SCOPE_LOCAL = 2; // 0x2
     field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
@@ -74,10 +83,14 @@
   public static interface TetheringManager.TetheringEventCallback {
     method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
     method public default void onError(@NonNull String, int);
+    method public default void onError(@NonNull android.net.TetheringInterface, int);
     method public default void onLocalOnlyInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+    method public default void onLocalOnlyInterfacesChanged(@NonNull java.util.Set<android.net.TetheringInterface>);
     method public default void onOffloadStatusChanged(int);
     method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+    method public default void onTetherableInterfacesChanged(@NonNull java.util.Set<android.net.TetheringInterface>);
     method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+    method public default void onTetheredInterfacesChanged(@NonNull java.util.Set<android.net.TetheringInterface>);
     method public default void onTetheringSupported(boolean);
     method public default void onUpstreamChanged(@Nullable android.net.Network);
   }
diff --git a/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl b/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl
index 3d842b3..43262fb 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl
+++ b/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl
@@ -16,15 +16,17 @@
 
 package android.net;
 
+import android.net.TetheringInterface;
+
 /**
  * Status details for tethering downstream interfaces.
  * {@hide}
  */
 parcelable TetherStatesParcel {
-    String[] availableList;
-    String[] tetheredList;
-    String[] localOnlyList;
-    String[] erroredIfaceList;
+    TetheringInterface[] availableList;
+    TetheringInterface[] tetheredList;
+    TetheringInterface[] localOnlyList;
+    TetheringInterface[] erroredIfaceList;
     // List of Last error code corresponding to each errored iface in erroredIfaceList. */
     // TODO: Improve this as b/143122247.
     int[] lastErrorList;
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringInterface.aidl b/Tethering/common/TetheringLib/src/android/net/TetheringInterface.aidl
new file mode 100644
index 0000000..7151984
--- /dev/null
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringInterface.aidl
@@ -0,0 +1,18 @@
+/*
+ * 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;
+
+@JavaOnlyStableParcelable parcelable TetheringInterface;
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringInterface.java b/Tethering/common/TetheringLib/src/android/net/TetheringInterface.java
new file mode 100644
index 0000000..84cdef1
--- /dev/null
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringInterface.java
@@ -0,0 +1,102 @@
+/*
+ * 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.net.TetheringManager.TetheringType;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * The mapping of tethering interface and type.
+ * @hide
+ */
+@SystemApi
+public final class TetheringInterface implements Parcelable {
+    private final int mType;
+    private final String mInterface;
+
+    public TetheringInterface(@TetheringType int type, @NonNull String iface) {
+        Objects.requireNonNull(iface);
+        mType = type;
+        mInterface = iface;
+    }
+
+    private TetheringInterface(@NonNull Parcel in) {
+        this(in.readInt(), in.readString());
+    }
+
+    /** Get tethering type. */
+    public int getType() {
+        return mType;
+    }
+
+    /** Get tethering interface. */
+    @NonNull
+    public String getInterface() {
+        return mInterface;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mType);
+        dest.writeString(mInterface);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mType, mInterface);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (!(obj instanceof TetheringInterface)) return false;
+        final TetheringInterface other = (TetheringInterface) obj;
+        return mType == other.mType && mInterface.equals(other.mInterface);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    public static final Creator<TetheringInterface> CREATOR = new Creator<TetheringInterface>() {
+        @NonNull
+        @Override
+        public TetheringInterface createFromParcel(@NonNull Parcel in) {
+            return new TetheringInterface(in);
+        }
+
+        @NonNull
+        @Override
+        public TetheringInterface[] newArray(int size) {
+            return new TetheringInterface[size];
+        }
+    };
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "TetheringInterface {mType=" + mType
+                + ", mInterface=" + mInterface + "}";
+    }
+}
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index c64da8a..73ab908 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -30,6 +30,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -43,6 +44,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.function.Supplier;
 
@@ -83,7 +85,10 @@
      * {@code TetheringManager.EXTRA_ERRORED_TETHER} to indicate
      * the current state of tethering.  Each include a list of
      * interface names in that state (may be empty).
+     *
+     * @deprecated New client should use TetheringEventCallback instead.
      */
+    @Deprecated
     public static final String ACTION_TETHER_STATE_CHANGED =
             "android.net.conn.TETHER_STATE_CHANGED";
 
@@ -531,7 +536,7 @@
     /**
      * Attempt to both alter the mode of USB and Tethering of USB.
      *
-     * @deprecated New client should not use this API anymore. All clients should use
+     * @deprecated New clients should not use this API anymore. All clients should use
      * #startTethering or #stopTethering which encapsulate proper entitlement logic. If the API is
      * used and an entitlement check is needed, downstream USB tethering will be enabled but will
      * not have any upstream.
@@ -978,8 +983,13 @@
          * multiple times later upon changes.
          * @param reg The new regular expressions.
          *
+         * @deprecated New clients should use the callbacks with {@link TetheringInterface} which
+         * has the mapping between tethering type and interface. InterfaceRegex is no longer needed
+         * to determine the mapping of tethering type and interface.
+         *
          * @hide
          */
+        @Deprecated
         @SystemApi(client = MODULE_LIBRARIES)
         default void onTetherableInterfaceRegexpsChanged(@NonNull TetheringInterfaceRegexps reg) {}
 
@@ -994,15 +1004,43 @@
         default void onTetherableInterfacesChanged(@NonNull List<String> interfaces) {}
 
         /**
+         * Called when there was a change in the list of tetherable interfaces. Tetherable
+         * interface means this interface is available and can be used for tethering.
+         *
+         * <p>This will be called immediately after the callback is registered, and may be called
+         * multiple times later upon changes.
+         * @param interfaces The set of TetheringInterface of currently tetherable interface.
+         */
+        default void onTetherableInterfacesChanged(@NonNull Set<TetheringInterface> interfaces) {
+            // By default, the new callback calls the old callback, so apps
+            // implementing the old callback just work.
+            onTetherableInterfacesChanged(toIfaces(interfaces));
+        }
+
+        /**
          * Called when there was a change in the list of tethered interfaces.
          *
          * <p>This will be called immediately after the callback is registered, and may be called
          * multiple times later upon changes.
-         * @param interfaces The list of 0 or more String of currently tethered interface names.
+         * @param interfaces The lit of 0 or more String of currently tethered interface names.
          */
         default void onTetheredInterfacesChanged(@NonNull List<String> interfaces) {}
 
         /**
+         * Called when there was a change in the list of tethered interfaces.
+         *
+         * <p>This will be called immediately after the callback is registered, and may be called
+         * multiple times later upon changes.
+         * @param interfaces The set of 0 or more TetheringInterface of currently tethered
+         * interface.
+         */
+        default void onTetheredInterfacesChanged(@NonNull Set<TetheringInterface> interfaces) {
+            // By default, the new callback calls the old callback, so apps
+            // implementing the old callback just work.
+            onTetheredInterfacesChanged(toIfaces(interfaces));
+        }
+
+        /**
          * Called when there was a change in the list of local-only interfaces.
          *
          * <p>This will be called immediately after the callback is registered, and may be called
@@ -1012,6 +1050,20 @@
         default void onLocalOnlyInterfacesChanged(@NonNull List<String> interfaces) {}
 
         /**
+         * Called when there was a change in the list of local-only interfaces.
+         *
+         * <p>This will be called immediately after the callback is registered, and may be called
+         * multiple times later upon changes.
+         * @param interfaces The set of 0 or more TetheringInterface of active local-only
+         * interface.
+         */
+        default void onLocalOnlyInterfacesChanged(@NonNull Set<TetheringInterface> interfaces) {
+            // By default, the new callback calls the old callback, so apps
+            // implementing the old callback just work.
+            onLocalOnlyInterfacesChanged(toIfaces(interfaces));
+        }
+
+        /**
          * Called when an error occurred configuring tethering.
          *
          * <p>This will be called immediately after the callback is registered if the latest status
@@ -1022,6 +1074,20 @@
         default void onError(@NonNull String ifName, @TetheringIfaceError int error) {}
 
         /**
+         * Called when an error occurred configuring tethering.
+         *
+         * <p>This will be called immediately after the callback is registered if the latest status
+         * on the interface is an error, and may be called multiple times later upon changes.
+         * @param iface The interface that experienced the error.
+         * @param error One of {@code TetheringManager#TETHER_ERROR_*}.
+         */
+        default void onError(@NonNull TetheringInterface iface, @TetheringIfaceError int error) {
+            // By default, the new callback calls the old callback, so apps
+            // implementing the old callback just work.
+            onError(iface.getInterface(), error);
+        }
+
+        /**
          * Called when the list of tethered clients changes.
          *
          * <p>This callback provides best-effort information on connected clients based on state
@@ -1044,9 +1110,37 @@
     }
 
     /**
-     * Regular expressions used to identify tethering interfaces.
+     * Covert DownStreamInterface collection to interface String array list. Internal use only.
+     *
      * @hide
      */
+    public static ArrayList<String> toIfaces(Collection<TetheringInterface> tetherIfaces) {
+        final ArrayList<String> ifaces = new ArrayList<>();
+        for (TetheringInterface tether : tetherIfaces) {
+            ifaces.add(tether.getInterface());
+        }
+
+        return ifaces;
+    }
+
+    private static String[] toIfaces(TetheringInterface[] tetherIfaces) {
+        final String[] ifaces = new String[tetherIfaces.length];
+        for (int i = 0; i < tetherIfaces.length; i++) {
+            ifaces[i] = tetherIfaces[i].getInterface();
+        }
+
+        return ifaces;
+    }
+
+
+    /**
+     * Regular expressions used to identify tethering interfaces.
+     *
+     * @deprecated Instead of using regex to determine tethering type. New client could use the
+     * callbacks with {@link TetheringInterface} which has the mapping of type and interface.
+     * @hide
+     */
+    @Deprecated
     @SystemApi(client = MODULE_LIBRARIES)
     public static class TetheringInterfaceRegexps {
         private final String[] mTetherableBluetoothRegexs;
@@ -1114,10 +1208,10 @@
             }
             final ITetheringEventCallback remoteCallback = new ITetheringEventCallback.Stub() {
                 // Only accessed with a lock on this object
-                private final HashMap<String, Integer> mErrorStates = new HashMap<>();
-                private String[] mLastTetherableInterfaces = null;
-                private String[] mLastTetheredInterfaces = null;
-                private String[] mLastLocalOnlyInterfaces = null;
+                private final HashMap<TetheringInterface, Integer> mErrorStates = new HashMap<>();
+                private TetheringInterface[] mLastTetherableInterfaces = null;
+                private TetheringInterface[] mLastTetheredInterfaces = null;
+                private TetheringInterface[] mLastLocalOnlyInterfaces = null;
 
                 @Override
                 public void onUpstreamChanged(Network network) throws RemoteException {
@@ -1128,14 +1222,14 @@
 
                 private synchronized void sendErrorCallbacks(final TetherStatesParcel newStates) {
                     for (int i = 0; i < newStates.erroredIfaceList.length; i++) {
-                        final String iface = newStates.erroredIfaceList[i];
-                        final Integer lastError = mErrorStates.get(iface);
+                        final TetheringInterface tetherIface = newStates.erroredIfaceList[i];
+                        final Integer lastError = mErrorStates.get(tetherIface);
                         final int newError = newStates.lastErrorList[i];
                         if (newError != TETHER_ERROR_NO_ERROR
                                 && !Objects.equals(lastError, newError)) {
-                            callback.onError(iface, newError);
+                            callback.onError(tetherIface, newError);
                         }
-                        mErrorStates.put(iface, newError);
+                        mErrorStates.put(tetherIface, newError);
                     }
                 }
 
@@ -1144,7 +1238,7 @@
                     if (Arrays.equals(mLastTetherableInterfaces, newStates.availableList)) return;
                     mLastTetherableInterfaces = newStates.availableList.clone();
                     callback.onTetherableInterfacesChanged(
-                            Collections.unmodifiableList(Arrays.asList(mLastTetherableInterfaces)));
+                            Collections.unmodifiableSet((new ArraySet(mLastTetherableInterfaces))));
                 }
 
                 private synchronized void maybeSendTetheredIfacesChangedCallback(
@@ -1152,7 +1246,7 @@
                     if (Arrays.equals(mLastTetheredInterfaces, newStates.tetheredList)) return;
                     mLastTetheredInterfaces = newStates.tetheredList.clone();
                     callback.onTetheredInterfacesChanged(
-                            Collections.unmodifiableList(Arrays.asList(mLastTetheredInterfaces)));
+                            Collections.unmodifiableSet((new ArraySet(mLastTetheredInterfaces))));
                 }
 
                 private synchronized void maybeSendLocalOnlyIfacesChangedCallback(
@@ -1160,7 +1254,7 @@
                     if (Arrays.equals(mLastLocalOnlyInterfaces, newStates.localOnlyList)) return;
                     mLastLocalOnlyInterfaces = newStates.localOnlyList.clone();
                     callback.onLocalOnlyInterfacesChanged(
-                            Collections.unmodifiableList(Arrays.asList(mLastLocalOnlyInterfaces)));
+                            Collections.unmodifiableSet((new ArraySet(mLastLocalOnlyInterfaces))));
                 }
 
                 // Called immediately after the callbacks are registered.
@@ -1262,8 +1356,8 @@
         if (mTetherStatesParcel == null) return TETHER_ERROR_NO_ERROR;
 
         int i = 0;
-        for (String errored : mTetherStatesParcel.erroredIfaceList) {
-            if (iface.equals(errored)) return mTetherStatesParcel.lastErrorList[i];
+        for (TetheringInterface errored : mTetherStatesParcel.erroredIfaceList) {
+            if (iface.equals(errored.getInterface())) return mTetherStatesParcel.lastErrorList[i];
 
             i++;
         }
@@ -1327,7 +1421,7 @@
         mCallback.waitForStarted();
         if (mTetherStatesParcel == null) return new String[0];
 
-        return mTetherStatesParcel.availableList;
+        return toIfaces(mTetherStatesParcel.availableList);
     }
 
     /**
@@ -1341,7 +1435,7 @@
         mCallback.waitForStarted();
         if (mTetherStatesParcel == null) return new String[0];
 
-        return mTetherStatesParcel.tetheredList;
+        return toIfaces(mTetherStatesParcel.tetheredList);
     }
 
     /**
@@ -1361,7 +1455,7 @@
         mCallback.waitForStarted();
         if (mTetherStatesParcel == null) return new String[0];
 
-        return mTetherStatesParcel.erroredIfaceList;
+        return toIfaces(mTetherStatesParcel.erroredIfaceList);
     }
 
     /**
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 0e8b2b5..7596380 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -51,6 +51,7 @@
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
+import static android.net.TetheringManager.toIfaces;
 import static android.net.util.TetheringMessageBase.BASE_MAIN_SM;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
@@ -92,6 +93,7 @@
 import android.net.TetheredClient;
 import android.net.TetheringCallbackStartedParcel;
 import android.net.TetheringConfigurationParcel;
+import android.net.TetheringInterface;
 import android.net.TetheringManager.TetheringRequest;
 import android.net.TetheringRequestParcel;
 import android.net.ip.IpServer;
@@ -143,7 +145,6 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
@@ -854,26 +855,27 @@
     private void sendTetherStateChangedBroadcast() {
         if (!isTetheringSupported()) return;
 
-        final ArrayList<String> availableList = new ArrayList<>();
-        final ArrayList<String> tetherList = new ArrayList<>();
-        final ArrayList<String> localOnlyList = new ArrayList<>();
-        final ArrayList<String> erroredList = new ArrayList<>();
-        final ArrayList<Integer> lastErrorList = new ArrayList<>();
+        final ArrayList<TetheringInterface> available = new ArrayList<>();
+        final ArrayList<TetheringInterface> tethered = new ArrayList<>();
+        final ArrayList<TetheringInterface> localOnly = new ArrayList<>();
+        final ArrayList<TetheringInterface> errored = new ArrayList<>();
+        final ArrayList<Integer> lastErrors = new ArrayList<>();
 
         final TetheringConfiguration cfg = mConfig;
-        mTetherStatesParcel = new TetherStatesParcel();
 
         int downstreamTypesMask = DOWNSTREAM_NONE;
         for (int i = 0; i < mTetherStates.size(); i++) {
-            TetherState tetherState = mTetherStates.valueAt(i);
-            String iface = mTetherStates.keyAt(i);
+            final TetherState tetherState = mTetherStates.valueAt(i);
+            final int type = tetherState.ipServer.interfaceType();
+            final String iface = mTetherStates.keyAt(i);
+            final TetheringInterface tetheringIface = new TetheringInterface(type, iface);
             if (tetherState.lastError != TETHER_ERROR_NO_ERROR) {
-                erroredList.add(iface);
-                lastErrorList.add(tetherState.lastError);
+                errored.add(tetheringIface);
+                lastErrors.add(tetherState.lastError);
             } else if (tetherState.lastState == IpServer.STATE_AVAILABLE) {
-                availableList.add(iface);
+                available.add(tetheringIface);
             } else if (tetherState.lastState == IpServer.STATE_LOCAL_ONLY) {
-                localOnlyList.add(iface);
+                localOnly.add(tetheringIface);
             } else if (tetherState.lastState == IpServer.STATE_TETHERED) {
                 if (cfg.isUsb(iface)) {
                     downstreamTypesMask |= (1 << TETHERING_USB);
@@ -882,40 +884,63 @@
                 } else if (cfg.isBluetooth(iface)) {
                     downstreamTypesMask |= (1 << TETHERING_BLUETOOTH);
                 }
-                tetherList.add(iface);
+                tethered.add(tetheringIface);
             }
         }
 
-        mTetherStatesParcel.availableList = availableList.toArray(new String[0]);
-        mTetherStatesParcel.tetheredList = tetherList.toArray(new String[0]);
-        mTetherStatesParcel.localOnlyList = localOnlyList.toArray(new String[0]);
-        mTetherStatesParcel.erroredIfaceList = erroredList.toArray(new String[0]);
-        mTetherStatesParcel.lastErrorList = new int[lastErrorList.size()];
-        Iterator<Integer> iterator = lastErrorList.iterator();
-        for (int i = 0; i < lastErrorList.size(); i++) {
-            mTetherStatesParcel.lastErrorList[i] = iterator.next().intValue();
-        }
+        mTetherStatesParcel = buildTetherStatesParcel(available, localOnly, tethered, errored,
+                lastErrors);
         reportTetherStateChanged(mTetherStatesParcel);
 
-        final Intent bcast = new Intent(ACTION_TETHER_STATE_CHANGED);
-        bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
-        bcast.putStringArrayListExtra(EXTRA_AVAILABLE_TETHER, availableList);
-        bcast.putStringArrayListExtra(EXTRA_ACTIVE_LOCAL_ONLY, localOnlyList);
-        bcast.putStringArrayListExtra(EXTRA_ACTIVE_TETHER, tetherList);
-        bcast.putStringArrayListExtra(EXTRA_ERRORED_TETHER, erroredList);
-        mContext.sendStickyBroadcastAsUser(bcast, UserHandle.ALL);
+        mContext.sendStickyBroadcastAsUser(buildStateChangeIntent(available, localOnly, tethered,
+                errored), UserHandle.ALL);
         if (DBG) {
             Log.d(TAG, String.format(
-                    "sendTetherStateChangedBroadcast %s=[%s] %s=[%s] %s=[%s] %s=[%s]",
-                    "avail", TextUtils.join(",", availableList),
-                    "local_only", TextUtils.join(",", localOnlyList),
-                    "tether", TextUtils.join(",", tetherList),
-                    "error", TextUtils.join(",", erroredList)));
+                    "reportTetherStateChanged %s=[%s] %s=[%s] %s=[%s] %s=[%s]",
+                    "avail", TextUtils.join(",", available),
+                    "local_only", TextUtils.join(",", localOnly),
+                    "tether", TextUtils.join(",", tethered),
+                    "error", TextUtils.join(",", errored)));
         }
 
         mNotificationUpdater.onDownstreamChanged(downstreamTypesMask);
     }
 
+    private TetherStatesParcel buildTetherStatesParcel(
+            final ArrayList<TetheringInterface> available,
+            final ArrayList<TetheringInterface> localOnly,
+            final ArrayList<TetheringInterface> tethered,
+            final ArrayList<TetheringInterface> errored,
+            final ArrayList<Integer> lastErrors) {
+        final TetherStatesParcel parcel = new TetherStatesParcel();
+
+        parcel.availableList = available.toArray(new TetheringInterface[0]);
+        parcel.tetheredList = tethered.toArray(new TetheringInterface[0]);
+        parcel.localOnlyList = localOnly.toArray(new TetheringInterface[0]);
+        parcel.erroredIfaceList = errored.toArray(new TetheringInterface[0]);
+        parcel.lastErrorList = new int[lastErrors.size()];
+        for (int i = 0; i < lastErrors.size(); i++) {
+            parcel.lastErrorList[i] = lastErrors.get(i);
+        }
+
+        return parcel;
+    }
+
+    private Intent buildStateChangeIntent(final ArrayList<TetheringInterface> available,
+            final ArrayList<TetheringInterface> localOnly,
+            final ArrayList<TetheringInterface> tethered,
+            final ArrayList<TetheringInterface> errored) {
+        final Intent bcast = new Intent(ACTION_TETHER_STATE_CHANGED);
+        bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+
+        bcast.putStringArrayListExtra(EXTRA_AVAILABLE_TETHER, toIfaces(available));
+        bcast.putStringArrayListExtra(EXTRA_ACTIVE_LOCAL_ONLY, toIfaces(localOnly));
+        bcast.putStringArrayListExtra(EXTRA_ACTIVE_TETHER, toIfaces(tethered));
+        bcast.putStringArrayListExtra(EXTRA_ERRORED_TETHER, toIfaces(errored));
+
+        return bcast;
+    }
+
     private class StateReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context content, Intent intent) {
@@ -2082,10 +2107,10 @@
 
     private TetherStatesParcel emptyTetherStatesParcel() {
         final TetherStatesParcel parcel = new TetherStatesParcel();
-        parcel.availableList = new String[0];
-        parcel.tetheredList = new String[0];
-        parcel.localOnlyList = new String[0];
-        parcel.erroredIfaceList = new String[0];
+        parcel.availableList = new TetheringInterface[0];
+        parcel.tetheredList = new TetheringInterface[0];
+        parcel.localOnlyList = new TetheringInterface[0];
+        parcel.erroredIfaceList = new TetheringInterface[0];
         parcel.lastErrorList = new int[0];
 
         return parcel;
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index de94cba..f1ddc6d 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -80,6 +80,7 @@
 import java.util.Collection;
 import java.util.List;
 import java.util.Random;
+import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -349,7 +350,7 @@
         private final CountDownLatch mLocalOnlyStartedLatch = new CountDownLatch(1);
         private final CountDownLatch mLocalOnlyStoppedLatch = new CountDownLatch(1);
         private final CountDownLatch mClientConnectedLatch = new CountDownLatch(1);
-        private final String mIface;
+        private final TetheringInterface mIface;
 
         private volatile boolean mInterfaceWasTethered = false;
         private volatile boolean mInterfaceWasLocalOnly = false;
@@ -358,20 +359,24 @@
 
         MyTetheringEventCallback(TetheringManager tm, String iface) {
             mTm = tm;
-            mIface = iface;
+            mIface = new TetheringInterface(TETHERING_ETHERNET, iface);
         }
 
         public void unregister() {
             mTm.unregisterTetheringEventCallback(this);
             mUnregistered = true;
         }
-
         @Override
         public void onTetheredInterfacesChanged(List<String> interfaces) {
+            fail("Should only call callback that takes a Set<TetheringInterface>");
+        }
+
+        @Override
+        public void onTetheredInterfacesChanged(Set<TetheringInterface> interfaces) {
             // Ignore stale callbacks registered by previous test cases.
             if (mUnregistered) return;
 
-            if (!mInterfaceWasTethered && (mIface == null || interfaces.contains(mIface))) {
+            if (!mInterfaceWasTethered && interfaces.contains(mIface)) {
                 // This interface is being tethered for the first time.
                 Log.d(TAG, "Tethering started: " + interfaces);
                 mInterfaceWasTethered = true;
@@ -384,10 +389,15 @@
 
         @Override
         public void onLocalOnlyInterfacesChanged(List<String> interfaces) {
+            fail("Should only call callback that takes a Set<TetheringInterface>");
+        }
+
+        @Override
+        public void onLocalOnlyInterfacesChanged(Set<TetheringInterface> interfaces) {
             // Ignore stale callbacks registered by previous test cases.
             if (mUnregistered) return;
 
-            if (!mInterfaceWasLocalOnly && (mIface == null || interfaces.contains(mIface))) {
+            if (!mInterfaceWasLocalOnly && interfaces.contains(mIface)) {
                 // This interface is being put into local-only mode for the first time.
                 Log.d(TAG, "Local-only started: " + interfaces);
                 mInterfaceWasLocalOnly = true;
diff --git a/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java b/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java
index 7ffe37a..07aab63 100644
--- a/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java
+++ b/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java
@@ -15,17 +15,18 @@
  */
 package android.tethering.mts;
 
+import static android.Manifest.permission.ACCESS_WIFI_STATE;
 import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
 import static android.Manifest.permission.NETWORK_SETTINGS;
 import static android.Manifest.permission.READ_DEVICE_CONFIG;
 import static android.Manifest.permission.TETHER_PRIVILEGED;
 import static android.Manifest.permission.WRITE_SETTINGS;
+import static android.net.TetheringManager.TETHERING_WIFI;
 import static android.net.cts.util.CtsTetheringUtils.isWifiTetheringSupported;
 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
 
 import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
@@ -35,6 +36,7 @@
 import android.content.Context;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
+import android.net.TetheringInterface;
 import android.net.TetheringManager;
 import android.net.cts.util.CtsTetheringUtils;
 import android.net.cts.util.CtsTetheringUtils.TestTetheringEventCallback;
@@ -69,7 +71,7 @@
     @Before
     public void setUp() throws Exception {
         mUiAutomation.adoptShellPermissionIdentity(MANAGE_TEST_NETWORKS, NETWORK_SETTINGS,
-                WRITE_SETTINGS, READ_DEVICE_CONFIG, TETHER_PRIVILEGED);
+                WRITE_SETTINGS, READ_DEVICE_CONFIG, TETHER_PRIVILEGED, ACCESS_WIFI_STATE);
         mContext = InstrumentationRegistry.getContext();
         mTm = mContext.getSystemService(TetheringManager.class);
         mCtsTetheringUtils = new CtsTetheringUtils(mContext);
@@ -101,13 +103,14 @@
         TestNetworkTracker tnt = null;
         try {
             tetherEventCallback.assumeTetheringSupported();
-            assumeTrue(isWifiTetheringSupported(tetherEventCallback));
+            assumeTrue(isWifiTetheringSupported(mContext, tetherEventCallback));
+            tetherEventCallback.expectNoTetheringActive();
 
-            mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
+            final TetheringInterface tetheredIface =
+                    mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
 
-            final List<String> tetheredIfaces = tetherEventCallback.getTetheredInterfaces();
-            assertEquals(1, tetheredIfaces.size());
-            final String wifiTetheringIface = tetheredIfaces.get(0);
+            assertNotNull(tetheredIface);
+            final String wifiTetheringIface = tetheredIface.getInterface();
 
             NetworkInterface nif = NetworkInterface.getByName(wifiTetheringIface);
             // Tethering downstream only have one ipv4 address.
@@ -120,11 +123,11 @@
             tnt = setUpTestNetwork(
                     new LinkAddress(testPrefix.getAddress(), testPrefix.getPrefixLength()));
 
-            tetherEventCallback.expectTetheredInterfacesChanged(null);
+            tetherEventCallback.expectNoTetheringActive();
             final List<String> wifiRegexs =
                     tetherEventCallback.getTetheringInterfaceRegexps().getTetherableWifiRegexs();
 
-            tetherEventCallback.expectTetheredInterfacesChanged(wifiRegexs);
+            tetherEventCallback.expectTetheredInterfacesChanged(wifiRegexs, TETHERING_WIFI);
             nif = NetworkInterface.getByName(wifiTetheringIface);
             final LinkAddress newHotspotAddr = getFirstIpv4Address(nif);
             assertNotNull(newHotspotAddr);
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 94a9238..5235eb7 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -130,6 +130,7 @@
 import android.net.TetheredClient.AddressInfo;
 import android.net.TetheringCallbackStartedParcel;
 import android.net.TetheringConfigurationParcel;
+import android.net.TetheringInterface;
 import android.net.TetheringRequestParcel;
 import android.net.dhcp.DhcpLeaseParcelable;
 import android.net.dhcp.DhcpServerCallbacks;
@@ -1793,6 +1794,8 @@
     public void testRegisterTetheringEventCallback() throws Exception {
         TestTetheringEventCallback callback = new TestTetheringEventCallback();
         TestTetheringEventCallback callback2 = new TestTetheringEventCallback();
+        final TetheringInterface wifiIface = new TetheringInterface(
+                TETHERING_WIFI, TEST_WLAN_IFNAME);
 
         // 1. Register one callback before running any tethering.
         mTethering.registerTetheringEventCallback(callback);
@@ -1811,12 +1814,12 @@
         mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
         mLooper.dispatchAll();
         tetherState = callback.pollTetherStatesChanged();
-        assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME});
+        assertArrayEquals(tetherState.availableList, new TetheringInterface[] {wifiIface});
 
         mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
         sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
         tetherState = callback.pollTetherStatesChanged();
-        assertArrayEquals(tetherState.tetheredList, new String[] {TEST_WLAN_IFNAME});
+        assertArrayEquals(tetherState.tetheredList, new TetheringInterface[] {wifiIface});
         callback.expectUpstreamChanged(upstreamState.network);
         callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STARTED);
 
@@ -1828,7 +1831,7 @@
         callback2.expectConfigurationChanged(
                 mTethering.getTetheringConfiguration().toStableParcelable());
         tetherState = callback2.pollTetherStatesChanged();
-        assertEquals(tetherState.tetheredList, new String[] {TEST_WLAN_IFNAME});
+        assertEquals(tetherState.tetheredList, new TetheringInterface[] {wifiIface});
         callback2.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STARTED);
 
         // 4. Unregister first callback and disable wifi tethering
@@ -1837,7 +1840,7 @@
         mTethering.stopTethering(TETHERING_WIFI);
         sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED);
         tetherState = callback2.pollTetherStatesChanged();
-        assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME});
+        assertArrayEquals(tetherState.availableList, new TetheringInterface[] {wifiIface});
         mLooper.dispatchAll();
         callback2.expectUpstreamChanged(new Network[] {null});
         callback2.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
diff --git a/framework/Android.bp b/framework/Android.bp
index c91017f..4fa9ccb 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -108,10 +108,8 @@
         "-Wthread-safety",
     ],
     shared_libs: [
-        "libbase",
         "liblog",
         "libnativehelper",
-        "libnetd_client",
     ],
     header_libs: [
         "dnsproxyd_protocol_headers",
@@ -124,6 +122,7 @@
     srcs: [
         "jni/android_net_NetworkUtils.cpp",
     ],
+    shared_libs: ["libandroid_net"],
     apex_available: [
         "//apex_available:platform",
         "com.android.tethering",
@@ -134,9 +133,11 @@
     name: "libframework-connectivity-jni",
     defaults: ["libframework-connectivity-defaults"],
     srcs: [
+        "jni/android_net_NetworkUtils.cpp",
         "jni/onload.cpp",
     ],
-    static_libs: ["libconnectivityframeworkutils"],
+    shared_libs: ["libandroid"],
+    stl: "libc++_static",
     apex_available: [
         "//apex_available:platform",
         "com.android.tethering",
diff --git a/framework/api/module-lib-current.txt b/framework/api/module-lib-current.txt
index 6c454bc..7fc0382 100644
--- a/framework/api/module-lib-current.txt
+++ b/framework/api/module-lib-current.txt
@@ -48,7 +48,6 @@
 
   public class ConnectivitySettingsManager {
     method public static void clearGlobalProxy(@NonNull android.content.Context);
-    method @NonNull public static java.util.Set<java.lang.String> getAppsAllowedOnRestrictedNetworks(@NonNull android.content.Context);
     method @Nullable public static String getCaptivePortalHttpUrl(@NonNull android.content.Context);
     method public static int getCaptivePortalMode(@NonNull android.content.Context, int);
     method @NonNull public static java.time.Duration getConnectivityKeepPendingIntentDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
@@ -66,9 +65,9 @@
     method @NonNull public static String getPrivateDnsDefaultMode(@NonNull android.content.Context);
     method @Nullable public static String getPrivateDnsHostname(@NonNull android.content.Context);
     method public static int getPrivateDnsMode(@NonNull android.content.Context);
+    method @NonNull public static java.util.Set<java.lang.Integer> getUidsAllowedOnRestrictedNetworks(@NonNull android.content.Context);
     method public static boolean getWifiAlwaysRequested(@NonNull android.content.Context, boolean);
     method @NonNull public static java.time.Duration getWifiDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
-    method public static void setAppsAllowedOnRestrictedNetworks(@NonNull android.content.Context, @NonNull java.util.Set<java.lang.String>);
     method public static void setCaptivePortalHttpUrl(@NonNull android.content.Context, @Nullable String);
     method public static void setCaptivePortalMode(@NonNull android.content.Context, int);
     method public static void setConnectivityKeepPendingIntentDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
@@ -86,6 +85,7 @@
     method public static void setPrivateDnsDefaultMode(@NonNull android.content.Context, @NonNull int);
     method public static void setPrivateDnsHostname(@NonNull android.content.Context, @Nullable String);
     method public static void setPrivateDnsMode(@NonNull android.content.Context, int);
+    method public static void setUidsAllowedOnRestrictedNetworks(@NonNull android.content.Context, @NonNull java.util.Set<java.lang.Integer>);
     method public static void setWifiAlwaysRequested(@NonNull android.content.Context, boolean);
     method public static void setWifiDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
     field public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; // 0x2
diff --git a/framework/jni/android_net_NetworkUtils.cpp b/framework/jni/android_net_NetworkUtils.cpp
index f17baf9..26484fb 100644
--- a/framework/jni/android_net_NetworkUtils.cpp
+++ b/framework/jni/android_net_NetworkUtils.cpp
@@ -19,6 +19,7 @@
 #include <vector>
 
 #include <android/file_descriptor_jni.h>
+#include <android/multinetwork.h>
 #include <arpa/inet.h>
 #include <linux/filter.h>
 #include <linux/if_arp.h>
@@ -36,7 +37,6 @@
 #include <utils/Log.h>
 #include <utils/misc.h>
 
-#include "NetdClient.h"
 #include "jni.h"
 
 extern "C" {
@@ -94,25 +94,32 @@
     }
 }
 
-static jboolean android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId)
+static jboolean android_net_utils_bindProcessToNetworkHandle(JNIEnv *env, jobject thiz,
+        jlong netHandle)
 {
-    return (jboolean) !setNetworkForProcess(netId);
+    return (jboolean) !android_setprocnetwork(netHandle);
 }
 
-static jint android_net_utils_getBoundNetworkForProcess(JNIEnv *env, jobject thiz)
+static jlong android_net_utils_getBoundNetworkHandleForProcess(JNIEnv *env, jobject thiz)
 {
-    return getNetworkForProcess();
+    net_handle_t network;
+    if (android_getprocnetwork(&network) != 0) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+                "android_getprocnetwork(): %s", strerror(errno));
+        return NETWORK_UNSPECIFIED;
+    }
+    return (jlong) network;
 }
 
 static jboolean android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz,
-        jint netId)
+        jint netId, jlong netHandle)
 {
-    return (jboolean) !setNetworkForResolv(netId);
+    return (jboolean) !android_setprocdns(netHandle);
 }
 
-static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jobject javaFd,
-                                                  jint netId) {
-    return setNetworkForSocket(netId, AFileDescriptor_getFd(env, javaFd));
+static jint android_net_utils_bindSocketToNetworkHandle(JNIEnv *env, jobject thiz, jobject javaFd,
+                                                  jlong netHandle) {
+    return android_setsocknetwork(netHandle, AFileDescriptor_getFd(env, javaFd));
 }
 
 static bool checkLenAndCopy(JNIEnv* env, const jbyteArray& addr, int len, void* dst)
@@ -124,7 +131,7 @@
     return true;
 }
 
-static jobject android_net_utils_resNetworkQuery(JNIEnv *env, jobject thiz, jint netId,
+static jobject android_net_utils_resNetworkQuery(JNIEnv *env, jobject thiz, jlong netHandle,
         jstring dname, jint ns_class, jint ns_type, jint flags) {
     const jsize javaCharsCount = env->GetStringLength(dname);
     const jsize byteCountUTF8 = env->GetStringUTFLength(dname);
@@ -134,7 +141,8 @@
     std::vector<char> queryname(byteCountUTF8 + 1, 0);
 
     env->GetStringUTFRegion(dname, 0, javaCharsCount, queryname.data());
-    int fd = resNetworkQuery(netId, queryname.data(), ns_class, ns_type, flags);
+
+    int fd = android_res_nquery(netHandle, queryname.data(), ns_class, ns_type, flags);
 
     if (fd < 0) {
         jniThrowErrnoException(env, "resNetworkQuery", -fd);
@@ -144,12 +152,12 @@
     return jniCreateFileDescriptor(env, fd);
 }
 
-static jobject android_net_utils_resNetworkSend(JNIEnv *env, jobject thiz, jint netId,
+static jobject android_net_utils_resNetworkSend(JNIEnv *env, jobject thiz, jlong netHandle,
         jbyteArray msg, jint msgLen, jint flags) {
     uint8_t data[MAXCMDSIZE];
 
     checkLenAndCopy(env, msg, msgLen, data);
-    int fd = resNetworkSend(netId, data, msgLen, flags);
+    int fd = android_res_nsend(netHandle, data, msgLen, flags);
 
     if (fd < 0) {
         jniThrowErrnoException(env, "resNetworkSend", -fd);
@@ -164,7 +172,7 @@
     int rcode;
     std::vector<uint8_t> buf(MAXPACKETSIZE, 0);
 
-    int res = resNetworkResult(fd, &rcode, buf.data(), MAXPACKETSIZE);
+    int res = android_res_nresult(fd, &rcode, buf.data(), MAXPACKETSIZE);
     jniSetFileDescriptorOfFD(env, javaFd, -1);
     if (res < 0) {
         jniThrowErrnoException(env, "resNetworkResult", -res);
@@ -188,23 +196,22 @@
 
 static void android_net_utils_resNetworkCancel(JNIEnv *env, jobject thiz, jobject javaFd) {
     int fd = AFileDescriptor_getFd(env, javaFd);
-    resNetworkCancel(fd);
+    android_res_cancel(fd);
     jniSetFileDescriptorOfFD(env, javaFd, -1);
 }
 
 static jobject android_net_utils_getDnsNetwork(JNIEnv *env, jobject thiz) {
-    unsigned dnsNetId = 0;
-    if (int res = getNetworkForDns(&dnsNetId) < 0) {
-        jniThrowErrnoException(env, "getDnsNetId", -res);
+    net_handle_t dnsNetHandle = NETWORK_UNSPECIFIED;
+    if (int res = android_getprocdns(&dnsNetHandle) < 0) {
+        jniThrowErrnoException(env, "getDnsNetwork", -res);
         return nullptr;
     }
-    bool privateDnsBypass = dnsNetId & NETID_USE_LOCAL_NAMESERVERS;
 
     static jclass class_Network = MakeGlobalRefOrDie(
             env, FindClassOrDie(env, "android/net/Network"));
-    static jmethodID ctor = env->GetMethodID(class_Network, "<init>", "(IZ)V");
-    return env->NewObject(
-            class_Network, ctor, dnsNetId & ~NETID_USE_LOCAL_NAMESERVERS, privateDnsBypass);
+    static jmethodID method = env->GetStaticMethodID(class_Network, "fromNetworkHandle",
+            "(J)Landroid/net/Network;");
+    return env->CallStaticObjectMethod(class_Network, method, static_cast<jlong>(dnsNetHandle));
 }
 
 static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) {
@@ -250,15 +257,15 @@
 // clang-format off
 static const JNINativeMethod gNetworkUtilMethods[] = {
     /* name, signature, funcPtr */
-    { "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork },
-    { "getBoundNetworkForProcess", "()I", (void*) android_net_utils_getBoundNetworkForProcess },
+    { "bindProcessToNetworkHandle", "(J)Z", (void*) android_net_utils_bindProcessToNetworkHandle },
+    { "getBoundNetworkHandleForProcess", "()J", (void*) android_net_utils_getBoundNetworkHandleForProcess },
     { "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
-    { "bindSocketToNetwork", "(Ljava/io/FileDescriptor;I)I", (void*) android_net_utils_bindSocketToNetwork },
+    { "bindSocketToNetworkHandle", "(Ljava/io/FileDescriptor;J)I", (void*) android_net_utils_bindSocketToNetworkHandle },
     { "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter },
     { "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter },
     { "getTcpRepairWindow", "(Ljava/io/FileDescriptor;)Landroid/net/TcpRepairWindow;", (void*) android_net_utils_getTcpRepairWindow },
-    { "resNetworkSend", "(I[BII)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkSend },
-    { "resNetworkQuery", "(ILjava/lang/String;III)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkQuery },
+    { "resNetworkSend", "(J[BII)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkSend },
+    { "resNetworkQuery", "(JLjava/lang/String;III)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkQuery },
     { "resNetworkResult", "(Ljava/io/FileDescriptor;)Landroid/net/DnsResolver$DnsResponse;", (void*) android_net_utils_resNetworkResult },
     { "resNetworkCancel", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_resNetworkCancel },
     { "getDnsNetwork", "()Landroid/net/Network;", (void*) android_net_utils_getDnsNetwork },
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index af8726e..a64a9e6 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -4917,7 +4917,7 @@
             InetAddressCompat.clearDnsCache();
             // Must flush socket pool as idle sockets will be bound to previous network and may
             // cause subsequent fetches to be performed on old network.
-            NetworkEventDispatcher.getInstance().onNetworkConfigurationChanged();
+            NetworkEventDispatcher.getInstance().dispatchNetworkConfigurationChange();
         }
 
         return true;
diff --git a/framework/src/android/net/ConnectivitySettingsManager.java b/framework/src/android/net/ConnectivitySettingsManager.java
index 762f24f..5f7f539 100644
--- a/framework/src/android/net/ConnectivitySettingsManager.java
+++ b/framework/src/android/net/ConnectivitySettingsManager.java
@@ -374,12 +374,12 @@
     private static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME_STRING = "hostname";
 
     /**
-     * A list of apps that is allowed on restricted networks.
+     * A list of uids that is allowed to use restricted networks.
      *
      * @hide
      */
-    public static final String APPS_ALLOWED_ON_RESTRICTED_NETWORKS =
-            "apps_allowed_on_restricted_networks";
+    public static final String UIDS_ALLOWED_ON_RESTRICTED_NETWORKS =
+            "uids_allowed_on_restricted_networks";
 
     /**
      * Get mobile data activity timeout from {@link Settings}.
@@ -1003,6 +1003,28 @@
                 context.getContentResolver(), NETWORK_METERED_MULTIPATH_PREFERENCE, preference);
     }
 
+    private static Set<Integer> getUidSetFromString(@Nullable String uidList) {
+        final Set<Integer> uids = new ArraySet<>();
+        if (TextUtils.isEmpty(uidList)) {
+            return uids;
+        }
+        for (String uid : uidList.split(";")) {
+            uids.add(Integer.valueOf(uid));
+        }
+        return uids;
+    }
+
+    private static String getUidStringFromSet(@NonNull Set<Integer> uidList) {
+        final StringJoiner joiner = new StringJoiner(";");
+        for (Integer uid : uidList) {
+            if (uid < 0 || UserHandle.getAppId(uid) > Process.LAST_APPLICATION_UID) {
+                throw new IllegalArgumentException("Invalid uid");
+            }
+            joiner.add(uid.toString());
+        }
+        return joiner.toString();
+    }
+
     /**
      * Get the list of uids(from {@link Settings}) that should go on cellular networks in preference
      * even when higher-priority networks are connected.
@@ -1015,14 +1037,7 @@
     public static Set<Integer> getMobileDataPreferredUids(@NonNull Context context) {
         final String uidList = Settings.Secure.getString(
                 context.getContentResolver(), MOBILE_DATA_PREFERRED_UIDS);
-        final Set<Integer> uids = new ArraySet<>();
-        if (TextUtils.isEmpty(uidList)) {
-            return uids;
-        }
-        for (String uid : uidList.split(";")) {
-            uids.add(Integer.valueOf(uid));
-        }
-        return uids;
+        return getUidSetFromString(uidList);
     }
 
     /**
@@ -1035,53 +1050,41 @@
      */
     public static void setMobileDataPreferredUids(@NonNull Context context,
             @NonNull Set<Integer> uidList) {
-        final StringJoiner joiner = new StringJoiner(";");
-        for (Integer uid : uidList) {
-            if (uid < 0 || UserHandle.getAppId(uid) > Process.LAST_APPLICATION_UID) {
-                throw new IllegalArgumentException("Invalid uid");
-            }
-            joiner.add(uid.toString());
-        }
-        Settings.Secure.putString(
-                context.getContentResolver(), MOBILE_DATA_PREFERRED_UIDS, joiner.toString());
+        final String uids = getUidStringFromSet(uidList);
+        Settings.Secure.putString(context.getContentResolver(), MOBILE_DATA_PREFERRED_UIDS, uids);
     }
 
     /**
-     * Get the list of apps(from {@link Settings}) that is allowed on restricted networks.
+     * Get the list of uids (from {@link Settings}) allowed to use restricted networks.
+     *
+     * Access to restricted networks is controlled by the (preinstalled-only)
+     * CONNECTIVITY_USE_RESTRICTED_NETWORKS permission, but highly privileged
+     * callers can also set a list of uids that can access restricted networks.
+     *
+     * This is useful for example in some jurisdictions where government apps,
+     * that can't be preinstalled, must still have access to emergency services.
      *
      * @param context The {@link Context} to query the setting.
-     * @return A list of apps that is allowed on restricted networks or null if no setting
+     * @return A list of uids that is allowed to use restricted networks or null if no setting
      *         value.
      */
     @NonNull
-    public static Set<String> getAppsAllowedOnRestrictedNetworks(@NonNull Context context) {
-        final String appList = Settings.Secure.getString(
-                context.getContentResolver(), APPS_ALLOWED_ON_RESTRICTED_NETWORKS);
-        if (TextUtils.isEmpty(appList)) {
-            return new ArraySet<>();
-        }
-        return new ArraySet<>(appList.split(";"));
+    public static Set<Integer> getUidsAllowedOnRestrictedNetworks(@NonNull Context context) {
+        final String uidList = Settings.Secure.getString(
+                context.getContentResolver(), UIDS_ALLOWED_ON_RESTRICTED_NETWORKS);
+        return getUidSetFromString(uidList);
     }
 
     /**
-     * Set the list of apps(from {@link Settings}) that is allowed on restricted networks.
-     *
-     * Note: Please refer to android developer guidelines for valid app(package name).
-     * https://developer.android.com/guide/topics/manifest/manifest-element.html#package
+     * Set the list of uids(from {@link Settings}) that is allowed to use restricted networks.
      *
      * @param context The {@link Context} to set the setting.
-     * @param list A list of apps that is allowed on restricted networks.
+     * @param uidList A list of uids that is allowed to use restricted networks.
      */
-    public static void setAppsAllowedOnRestrictedNetworks(@NonNull Context context,
-            @NonNull Set<String> list) {
-        final StringJoiner joiner = new StringJoiner(";");
-        for (String app : list) {
-            if (app == null || app.contains(";")) {
-                throw new IllegalArgumentException("Invalid app(package name)");
-            }
-            joiner.add(app);
-        }
-        Settings.Secure.putString(context.getContentResolver(), APPS_ALLOWED_ON_RESTRICTED_NETWORKS,
-                joiner.toString());
+    public static void setUidsAllowedOnRestrictedNetworks(@NonNull Context context,
+            @NonNull Set<Integer> uidList) {
+        final String uids = getUidStringFromSet(uidList);
+        Settings.Secure.putString(context.getContentResolver(), UIDS_ALLOWED_ON_RESTRICTED_NETWORKS,
+                uids);
     }
 }
diff --git a/framework/src/android/net/Network.java b/framework/src/android/net/Network.java
index 41fad63..1f49033 100644
--- a/framework/src/android/net/Network.java
+++ b/framework/src/android/net/Network.java
@@ -92,6 +92,7 @@
     // value in the native/android/net.c NDK implementation.
     private static final long HANDLE_MAGIC = 0xcafed00dL;
     private static final int HANDLE_MAGIC_SIZE = 32;
+    private static final int USE_LOCAL_NAMESERVERS_FLAG = 0x80000000;
 
     // A boolean to control how getAllByName()/getByName() behaves in the face
     // of Private DNS.
@@ -189,7 +190,7 @@
      */
     public int getNetIdForResolv() {
         return mPrivateDnsBypass
-                ? (int) (0x80000000L | (long) netId)  // Non-portable DNS resolution flag.
+                ? (USE_LOCAL_NAMESERVERS_FLAG | netId)  // Non-portable DNS resolution flag.
                 : netId;
     }
 
@@ -452,12 +453,13 @@
             throw new IllegalArgumentException(
                     "Network.fromNetworkHandle refusing to instantiate NETID_UNSET Network.");
         }
-        if ((networkHandle & ((1L << HANDLE_MAGIC_SIZE) - 1)) != HANDLE_MAGIC
-                || networkHandle < 0) {
+        if ((networkHandle & ((1L << HANDLE_MAGIC_SIZE) - 1)) != HANDLE_MAGIC) {
             throw new IllegalArgumentException(
                     "Value passed to fromNetworkHandle() is not a network handle.");
         }
-        return new Network((int) (networkHandle >> HANDLE_MAGIC_SIZE));
+        final int netIdForResolv = (int) (networkHandle >>> HANDLE_MAGIC_SIZE);
+        return new Network((netIdForResolv & ~USE_LOCAL_NAMESERVERS_FLAG),
+                ((netIdForResolv & USE_LOCAL_NAMESERVERS_FLAG) != 0) /* privateDnsBypass */);
     }
 
     /**
@@ -485,7 +487,7 @@
         if (netId == 0) {
             return 0L;  // make this zero condition obvious for debugging
         }
-        return (((long) netId) << HANDLE_MAGIC_SIZE) | HANDLE_MAGIC;
+        return (((long) getNetIdForResolv()) << HANDLE_MAGIC_SIZE) | HANDLE_MAGIC;
     }
 
     // implement the Parcelable interface
diff --git a/framework/src/android/net/NetworkUtils.java b/framework/src/android/net/NetworkUtils.java
index a92fda1..16a49bc 100644
--- a/framework/src/android/net/NetworkUtils.java
+++ b/framework/src/android/net/NetworkUtils.java
@@ -16,6 +16,8 @@
 
 package android.net;
 
+import static android.net.ConnectivityManager.NETID_UNSET;
+
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.system.ErrnoException;
@@ -55,6 +57,8 @@
      */
     public static native void detachBPFFilter(FileDescriptor fd) throws SocketException;
 
+    private static native boolean bindProcessToNetworkHandle(long netHandle);
+
     /**
      * Binds the current process to the network designated by {@code netId}.  All sockets created
      * in the future (and not explicitly bound via a bound {@link SocketFactory} (see
@@ -63,13 +67,20 @@
      * is by design so an application doesn't accidentally use sockets it thinks are still bound to
      * a particular {@code Network}.  Passing NETID_UNSET clears the binding.
      */
-    public native static boolean bindProcessToNetwork(int netId);
+    public static boolean bindProcessToNetwork(int netId) {
+        return bindProcessToNetworkHandle(new Network(netId).getNetworkHandle());
+    }
+
+    private static native long getBoundNetworkHandleForProcess();
 
     /**
      * Return the netId last passed to {@link #bindProcessToNetwork}, or NETID_UNSET if
      * {@link #unbindProcessToNetwork} has been called since {@link #bindProcessToNetwork}.
      */
-    public native static int getBoundNetworkForProcess();
+    public static int getBoundNetworkForProcess() {
+        final long netHandle = getBoundNetworkHandleForProcess();
+        return netHandle == 0L ? NETID_UNSET : Network.fromNetworkHandle(netHandle).getNetId();
+    }
 
     /**
      * Binds host resolutions performed by this process to the network designated by {@code netId}.
@@ -81,12 +92,16 @@
     @Deprecated
     public native static boolean bindProcessToNetworkForHostResolution(int netId);
 
+    private static native int bindSocketToNetworkHandle(FileDescriptor fd, long netHandle);
+
     /**
      * Explicitly binds {@code fd} to the network designated by {@code netId}.  This
      * overrides any binding via {@link #bindProcessToNetwork}.
      * @return 0 on success or negative errno on failure.
      */
-    public static native int bindSocketToNetwork(FileDescriptor fd, int netId);
+    public static int bindSocketToNetwork(FileDescriptor fd, int netId) {
+        return bindSocketToNetworkHandle(fd, new Network(netId).getNetworkHandle());
+    }
 
     /**
      * Determine if {@code uid} can access network designated by {@code netId}.
@@ -97,14 +112,22 @@
         return false;
     }
 
+    private static native FileDescriptor resNetworkSend(
+            long netHandle, byte[] msg, int msglen, int flags) throws ErrnoException;
+
     /**
      * DNS resolver series jni method.
      * Issue the query {@code msg} on the network designated by {@code netId}.
      * {@code flags} is an additional config to control actual querying behavior.
      * @return a file descriptor to watch for read events
      */
-    public static native FileDescriptor resNetworkSend(
-            int netId, byte[] msg, int msglen, int flags) throws ErrnoException;
+    public static FileDescriptor resNetworkSend(
+            int netId, byte[] msg, int msglen, int flags) throws ErrnoException {
+        return resNetworkSend(new Network(netId).getNetworkHandle(), msg, msglen, flags);
+    }
+
+    private static native FileDescriptor resNetworkQuery(
+            long netHandle, String dname, int nsClass, int nsType, int flags) throws ErrnoException;
 
     /**
      * DNS resolver series jni method.
@@ -113,8 +136,11 @@
      * {@code flags} is an additional config to control actual querying behavior.
      * @return a file descriptor to watch for read events
      */
-    public static native FileDescriptor resNetworkQuery(
-            int netId, String dname, int nsClass, int nsType, int flags) throws ErrnoException;
+    public static FileDescriptor resNetworkQuery(
+            int netId, String dname, int nsClass, int nsType, int flags) throws ErrnoException {
+        return resNetworkQuery(new Network(netId).getNetworkHandle(), dname, nsClass, nsType,
+                flags);
+    }
 
     /**
      * DNS resolver series jni method.
diff --git a/service/Android.bp b/service/Android.bp
index b6adf79..8c46dea 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -32,6 +32,7 @@
         "jni/com_android_server_TestNetworkService.cpp",
         "jni/onload.cpp",
     ],
+    stl: "libc++_static",
     shared_libs: [
         "libbase",
         "liblog",
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index c5ad135..aa1648f 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -87,7 +87,6 @@
 import static java.util.Map.Entry;
 
 import android.Manifest;
-import android.annotation.BoolRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
@@ -150,7 +149,6 @@
 import android.net.NetworkScore;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
-import android.net.NetworkStackClient;
 import android.net.NetworkState;
 import android.net.NetworkStateSnapshot;
 import android.net.NetworkTestResultParcelable;
@@ -177,13 +175,14 @@
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.NetworkEvent;
 import android.net.netlink.InetDiagMessage;
+import android.net.networkstack.ModuleNetworkStackClient;
+import android.net.networkstack.NetworkStackClientBase;
 import android.net.resolv.aidl.DnsHealthEventParcel;
 import android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener;
 import android.net.resolv.aidl.Nat64PrefixEventParcel;
 import android.net.resolv.aidl.PrivateDnsValidationEventParcel;
 import android.net.shared.PrivateDnsConfig;
 import android.net.util.MultinetworkPolicyTracker;
-import android.net.util.NetdService;
 import android.os.BatteryStatsManager;
 import android.os.Binder;
 import android.os.Build;
@@ -1184,10 +1183,10 @@
         }
 
         /**
-         * Get a reference to the NetworkStackClient.
+         * Get a reference to the ModuleNetworkStackClient.
          */
-        public NetworkStackClient getNetworkStack() {
-            return NetworkStackClient.getInstance();
+        public NetworkStackClientBase getNetworkStack() {
+            return ModuleNetworkStackClient.getInstance(null);
         }
 
         /**
@@ -1246,7 +1245,8 @@
 
     public ConnectivityService(Context context) {
         this(context, getDnsResolver(context), new IpConnectivityLog(),
-                NetdService.getInstance(), new Dependencies());
+                INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)),
+                new Dependencies());
     }
 
     @VisibleForTesting
@@ -1460,7 +1460,7 @@
         mHandler.sendEmptyMessage(EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
     }
 
-    private void handleAlwaysOnNetworkRequest(NetworkRequest networkRequest, @BoolRes int id) {
+    private void handleAlwaysOnNetworkRequest(NetworkRequest networkRequest, int id) {
         final boolean enable = mContext.getResources().getBoolean(id);
         handleAlwaysOnNetworkRequest(networkRequest, enable);
     }
@@ -2989,10 +2989,6 @@
         }
 
         pw.println();
-        pw.println("NetworkStackClient logs:");
-        pw.increaseIndent();
-        NetworkStackClient.getInstance().dump(pw);
-        pw.decreaseIndent();
 
         pw.println();
         pw.println("Permission Monitor:");
@@ -8784,7 +8780,7 @@
         }
     }
 
-    private @VpnManager.VpnType int getVpnType(@Nullable NetworkAgentInfo vpn) {
+    private int getVpnType(@Nullable NetworkAgentInfo vpn) {
         if (vpn == null) return VpnManager.TYPE_VPN_NONE;
         final TransportInfo ti = vpn.networkCapabilities.getTransportInfo();
         if (!(ti instanceof VpnTransportInfo)) return VpnManager.TYPE_VPN_NONE;
diff --git a/service/src/com/android/server/TestNetworkService.java b/service/src/com/android/server/TestNetworkService.java
index f566277..09873f4 100644
--- a/service/src/com/android/server/TestNetworkService.java
+++ b/service/src/com/android/server/TestNetworkService.java
@@ -35,7 +35,6 @@
 import android.net.RouteInfo;
 import android.net.TestNetworkInterface;
 import android.net.TestNetworkSpecifier;
-import android.net.util.NetdService;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -86,7 +85,9 @@
         mHandler = new Handler(mHandlerThread.getLooper());
 
         mContext = Objects.requireNonNull(context, "missing Context");
-        mNetd = Objects.requireNonNull(NetdService.getInstance(), "could not get netd instance");
+        mNetd = Objects.requireNonNull(
+                INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)),
+                "could not get netd instance");
         mCm = mContext.getSystemService(ConnectivityManager.class);
         mNetworkProvider = new NetworkProvider(mContext, mHandler.getLooper(),
                 TEST_NETWORK_PROVIDER_NAME);
diff --git a/service/src/com/android/server/connectivity/NetworkNotificationManager.java b/service/src/com/android/server/connectivity/NetworkNotificationManager.java
index 0c0d459..b57ad5d 100644
--- a/service/src/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/service/src/com/android/server/connectivity/NetworkNotificationManager.java
@@ -84,7 +84,7 @@
 
     // The context is for the current user (system server)
     private final Context mContext;
-    private final Resources mResources;
+    private final ConnectivityResources mResources;
     private final TelephonyManager mTelephonyManager;
     // The notification manager is created from a context for User.ALL, so notifications
     // will be sent to all users.
@@ -99,7 +99,7 @@
                 (NotificationManager) c.createContextAsUser(UserHandle.ALL, 0 /* flags */)
                         .getSystemService(Context.NOTIFICATION_SERVICE);
         mNotificationTypeMap = new SparseIntArray();
-        mResources = new ConnectivityResources(mContext).get();
+        mResources = new ConnectivityResources(mContext);
     }
 
     @VisibleForTesting
@@ -118,11 +118,11 @@
     }
 
     private String getTransportName(final int transportType) {
-        String[] networkTypes = mResources.getStringArray(R.array.network_switch_type_name);
+        String[] networkTypes = mResources.get().getStringArray(R.array.network_switch_type_name);
         try {
             return networkTypes[transportType];
         } catch (IndexOutOfBoundsException e) {
-            return mResources.getString(R.string.network_switch_type_name_unknown);
+            return mResources.get().getString(R.string.network_switch_type_name_unknown);
         }
     }
 
@@ -197,10 +197,11 @@
                     tag, nameOf(eventId), getTransportName(transportType), name, highPriority));
         }
 
-        final Resources r = mResources;
+        final Resources r = mResources.get();
         final CharSequence title;
         final CharSequence details;
-        Icon icon = Icon.createWithResource(r, getIcon(transportType));
+        Icon icon = Icon.createWithResource(
+                mResources.getResourcesContext(), getIcon(transportType));
         if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
             title = r.getString(R.string.wifi_no_internet, name);
             details = r.getString(R.string.wifi_no_internet_detailed);
@@ -355,7 +356,7 @@
     public void showToast(NetworkAgentInfo fromNai, NetworkAgentInfo toNai) {
         String fromTransport = getTransportName(approximateTransportType(fromNai));
         String toTransport = getTransportName(approximateTransportType(toNai));
-        String text = mResources.getString(
+        String text = mResources.get().getString(
                 R.string.network_switch_metered_toast, fromTransport, toTransport);
         Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
     }
diff --git a/service/src/com/android/server/connectivity/OsCompat.java b/service/src/com/android/server/connectivity/OsCompat.java
new file mode 100644
index 0000000..57e3dcd
--- /dev/null
+++ b/service/src/com/android/server/connectivity/OsCompat.java
@@ -0,0 +1,75 @@
+/*
+ * 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 com.android.server.connectivity;
+
+import android.system.ErrnoException;
+import android.system.Os;
+
+import java.io.FileDescriptor;
+
+/**
+ * Compatibility utility for android.system.Os core platform APIs.
+ *
+ * Connectivity has access to such APIs, but they are not part of the module_current stubs yet
+ * (only core_current). Most stable core platform APIs are included manually in the connectivity
+ * build rules, but because Os is also part of the base java SDK that is earlier on the
+ * classpath, the extra core platform APIs are not seen.
+ *
+ * TODO (b/157639992, b/183097033): remove as soon as core_current is part of system_server_current
+ * @hide
+ */
+public class OsCompat {
+    // This value should be correct on all architectures supported by Android, but hardcoding ioctl
+    // numbers should be avoided.
+    /**
+     * @see android.system.OsConstants#TIOCOUTQ
+     */
+    public static final int TIOCOUTQ = 0x5411;
+
+    /**
+     * @see android.system.Os#getsockoptInt(FileDescriptor, int, int)
+     */
+    public static int getsockoptInt(FileDescriptor fd, int level, int option) throws
+            ErrnoException {
+        try {
+            return (int) Os.class.getMethod(
+                    "getsockoptInt", FileDescriptor.class, int.class, int.class)
+                    .invoke(null, fd, level, option);
+        } catch (ReflectiveOperationException e) {
+            if (e.getCause() instanceof ErrnoException) {
+                throw (ErrnoException) e.getCause();
+            }
+            throw new IllegalStateException("Error calling getsockoptInt", e);
+        }
+    }
+
+    /**
+     * @see android.system.Os#ioctlInt(FileDescriptor, int)
+     */
+    public static int ioctlInt(FileDescriptor fd, int cmd) throws
+            ErrnoException {
+        try {
+            return (int) Os.class.getMethod(
+                    "ioctlInt", FileDescriptor.class, int.class).invoke(null, fd, cmd);
+        } catch (ReflectiveOperationException e) {
+            if (e.getCause() instanceof ErrnoException) {
+                throw (ErrnoException) e.getCause();
+            }
+            throw new IllegalStateException("Error calling ioctlInt", e);
+        }
+    }
+}
diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java
index 673c804..49b43f8 100644
--- a/service/src/com/android/server/connectivity/PermissionMonitor.java
+++ b/service/src/com/android/server/connectivity/PermissionMonitor.java
@@ -24,7 +24,7 @@
 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.APPS_ALLOWED_ON_RESTRICTED_NETWORKS;
+import static android.net.ConnectivitySettingsManager.UIDS_ALLOWED_ON_RESTRICTED_NETWORKS;
 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
 import static android.os.Process.INVALID_UID;
 import static android.os.Process.SYSTEM_UID;
@@ -109,13 +109,13 @@
     @GuardedBy("this")
     private final Set<Integer> mAllApps = new HashSet<>();
 
-    // A set of apps which are allowed to use restricted networks. These apps can't hold the
-    // CONNECTIVITY_USE_RESTRICTED_NETWORKS permission because they can't be signature|privileged
-    // apps. However, these apps should still be able to use restricted networks under certain
-    // conditions (e.g. government app using emergency services). So grant netd system permission
-    // to uids whose package name is listed in APPS_ALLOWED_ON_RESTRICTED_NETWORKS setting.
+    // A set of uids which are allowed to use restricted networks. The packages of these uids can't
+    // hold the CONNECTIVITY_USE_RESTRICTED_NETWORKS permission because they can't be
+    // signature|privileged apps. However, these apps should still be able to use restricted
+    // networks under certain conditions (e.g. government app using emergency services). So grant
+    // netd system permission to these uids which is listed in UIDS_ALLOWED_ON_RESTRICTED_NETWORKS.
     @GuardedBy("this")
-    private final Set<String> mAppsAllowedOnRestrictedNetworks = new ArraySet<>();
+    private final Set<Integer> mUidsAllowedOnRestrictedNetworks = new ArraySet<>();
 
     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
@@ -149,10 +149,10 @@
         }
 
         /**
-         * Get apps allowed to use restricted networks via ConnectivitySettingsManager.
+         * Get uids allowed to use restricted networks via ConnectivitySettingsManager.
          */
-        public Set<String> getAppsAllowedOnRestrictedNetworks(@NonNull Context context) {
-            return ConnectivitySettingsManager.getAppsAllowedOnRestrictedNetworks(context);
+        public Set<Integer> getUidsAllowedOnRestrictedNetworks(@NonNull Context context) {
+            return ConnectivitySettingsManager.getUidsAllowedOnRestrictedNetworks(context);
         }
 
         /**
@@ -194,10 +194,10 @@
                 mIntentReceiver, intentFilter, null /* broadcastPermission */,
                 null /* scheduler */);
 
-        // Register APPS_ALLOWED_ON_RESTRICTED_NETWORKS setting observer
+        // Register UIDS_ALLOWED_ON_RESTRICTED_NETWORKS setting observer
         mDeps.registerContentObserver(
                 userAllContext,
-                Settings.Secure.getUriFor(APPS_ALLOWED_ON_RESTRICTED_NETWORKS),
+                Settings.Secure.getUriFor(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS),
                 false /* notifyForDescendants */,
                 new ContentObserver(null) {
                     @Override
@@ -206,9 +206,9 @@
                     }
                 });
 
-        // Read APPS_ALLOWED_ON_RESTRICTED_NETWORKS setting and update
-        // mAppsAllowedOnRestrictedNetworks.
-        updateAppsAllowedOnRestrictedNetworks(mDeps.getAppsAllowedOnRestrictedNetworks(mContext));
+        // Read UIDS_ALLOWED_ON_RESTRICTED_NETWORKS setting and update
+        // mUidsAllowedOnRestrictedNetworks.
+        updateUidsAllowedOnRestrictedNetworks(mDeps.getUidsAllowedOnRestrictedNetworks(mContext));
 
         List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS
                 | MATCH_ANY_USER);
@@ -265,9 +265,9 @@
     }
 
     @VisibleForTesting
-    void updateAppsAllowedOnRestrictedNetworks(final Set<String> apps) {
-        mAppsAllowedOnRestrictedNetworks.clear();
-        mAppsAllowedOnRestrictedNetworks.addAll(apps);
+    void updateUidsAllowedOnRestrictedNetworks(final Set<Integer> uids) {
+        mUidsAllowedOnRestrictedNetworks.clear();
+        mUidsAllowedOnRestrictedNetworks.addAll(uids);
     }
 
     @VisibleForTesting
@@ -285,10 +285,11 @@
     }
 
     @VisibleForTesting
-    boolean isAppAllowedOnRestrictedNetworks(@NonNull final PackageInfo app) {
-        // Check whether package name is in allowed on restricted networks app list. If so, this app
-        // can have netd system permission.
-        return mAppsAllowedOnRestrictedNetworks.contains(app.packageName);
+    boolean isUidAllowedOnRestrictedNetworks(final ApplicationInfo appInfo) {
+        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(appInfo.uid);
     }
 
     @VisibleForTesting
@@ -310,7 +311,8 @@
     boolean hasRestrictedNetworkPermission(@NonNull final PackageInfo app) {
         // TODO : remove carryover package check in the future(b/31479477). All apps should just
         //  request the appropriate permission for their use case since android Q.
-        return isCarryoverPackage(app.applicationInfo) || isAppAllowedOnRestrictedNetworks(app)
+        return isCarryoverPackage(app.applicationInfo)
+                || isUidAllowedOnRestrictedNetworks(app.applicationInfo)
                 || hasPermission(app, PERMISSION_MAINLINE_NETWORK_STACK)
                 || hasPermission(app, NETWORK_STACK)
                 || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
@@ -770,35 +772,31 @@
     }
 
     private synchronized void onSettingChanged() {
-        // Step1. Update apps allowed to use restricted networks and compute the set of packages to
+        // Step1. Update uids allowed to use restricted networks and compute the set of uids to
         // update.
-        final Set<String> packagesToUpdate = new ArraySet<>(mAppsAllowedOnRestrictedNetworks);
-        updateAppsAllowedOnRestrictedNetworks(mDeps.getAppsAllowedOnRestrictedNetworks(mContext));
-        packagesToUpdate.addAll(mAppsAllowedOnRestrictedNetworks);
+        final Set<Integer> uidsToUpdate = new ArraySet<>(mUidsAllowedOnRestrictedNetworks);
+        updateUidsAllowedOnRestrictedNetworks(mDeps.getUidsAllowedOnRestrictedNetworks(mContext));
+        uidsToUpdate.addAll(mUidsAllowedOnRestrictedNetworks);
 
-        final Map<Integer, Boolean> updatedApps = new HashMap<>();
-        final Map<Integer, Boolean> removedApps = new HashMap<>();
+        final Map<Integer, Boolean> updatedUids = new HashMap<>();
+        final Map<Integer, Boolean> removedUids = new HashMap<>();
 
-        // Step2. For each package to update, find out its new permission.
-        for (String app : packagesToUpdate) {
-            final PackageInfo info = getPackageInfo(app);
-            if (info == null || info.applicationInfo == null) continue;
-
-            final int uid = info.applicationInfo.uid;
+        // Step2. For each uid to update, find out its new permission.
+        for (Integer uid : uidsToUpdate) {
             final Boolean permission = highestUidNetworkPermission(uid);
 
             if (null == permission) {
-                removedApps.put(uid, NETWORK); // Doesn't matter which permission is set here.
+                removedUids.put(uid, NETWORK); // Doesn't matter which permission is set here.
                 mApps.remove(uid);
             } else {
-                updatedApps.put(uid, permission);
+                updatedUids.put(uid, permission);
                 mApps.put(uid, permission);
             }
         }
 
         // Step3. Update or revoke permission for uids with netd.
-        update(mUsers, updatedApps, true /* add */);
-        update(mUsers, removedApps, false /* add */);
+        update(mUsers, updatedUids, true /* add */);
+        update(mUsers, removedUids, false /* add */);
     }
 
     /** Dump info to dumpsys */
diff --git a/service/src/com/android/server/connectivity/TcpKeepaliveController.java b/service/src/com/android/server/connectivity/TcpKeepaliveController.java
index c480594..73f3475 100644
--- a/service/src/com/android/server/connectivity/TcpKeepaliveController.java
+++ b/service/src/com/android/server/connectivity/TcpKeepaliveController.java
@@ -27,7 +27,8 @@
 import static android.system.OsConstants.IPPROTO_TCP;
 import static android.system.OsConstants.IP_TOS;
 import static android.system.OsConstants.IP_TTL;
-import static android.system.OsConstants.TIOCOUTQ;
+
+import static com.android.server.connectivity.OsCompat.TIOCOUTQ;
 
 import android.annotation.NonNull;
 import android.net.InvalidPacketException;
@@ -175,10 +176,10 @@
             }
             // Query write sequence number from SEND_QUEUE.
             Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR_QUEUE, TCP_SEND_QUEUE);
-            tcpDetails.seq = Os.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ);
+            tcpDetails.seq = OsCompat.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ);
             // Query read sequence number from RECV_QUEUE.
             Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR_QUEUE, TCP_RECV_QUEUE);
-            tcpDetails.ack = Os.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ);
+            tcpDetails.ack = OsCompat.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ);
             // Switch to NO_QUEUE to prevent illegal socket read/write in repair mode.
             Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR_QUEUE, TCP_NO_QUEUE);
             // Finally, check if socket is still idle. TODO : this check needs to move to
@@ -198,9 +199,9 @@
             tcpDetails.rcvWndScale = trw.rcvWndScale;
             if (tcpDetails.srcAddress.length == 4 /* V4 address length */) {
                 // Query TOS.
-                tcpDetails.tos = Os.getsockoptInt(fd, IPPROTO_IP, IP_TOS);
+                tcpDetails.tos = OsCompat.getsockoptInt(fd, IPPROTO_IP, IP_TOS);
                 // Query TTL.
-                tcpDetails.ttl = Os.getsockoptInt(fd, IPPROTO_IP, IP_TTL);
+                tcpDetails.ttl = OsCompat.getsockoptInt(fd, IPPROTO_IP, IP_TTL);
             }
         } catch (ErrnoException e) {
             Log.e(TAG, "Exception reading TCP state from socket", e);
@@ -305,7 +306,7 @@
 
     private static boolean isReceiveQueueEmpty(FileDescriptor fd)
             throws ErrnoException {
-        final int result = Os.ioctlInt(fd, SIOCINQ);
+        final int result = OsCompat.ioctlInt(fd, SIOCINQ);
         if (result != 0) {
             Log.e(TAG, "Read queue has data");
             return false;
@@ -315,7 +316,7 @@
 
     private static boolean isSendQueueEmpty(FileDescriptor fd)
             throws ErrnoException {
-        final int result = Os.ioctlInt(fd, SIOCOUTQ);
+        final int result = OsCompat.ioctlInt(fd, SIOCOUTQ);
         if (result != 0) {
             Log.e(TAG, "Write queue has data");
             return false;
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 8023aa0..33f704f 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -49,11 +49,16 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.TRANSPORT_TEST;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.net.TetheringManager.TetheringRequest;
 import static android.net.cts.util.CtsNetUtils.ConnectivityActionReceiver;
 import static android.net.cts.util.CtsNetUtils.HTTP_PORT;
 import static android.net.cts.util.CtsNetUtils.NETWORK_CALLBACK_ACTION;
 import static android.net.cts.util.CtsNetUtils.TEST_HOST;
 import static android.net.cts.util.CtsNetUtils.TestNetworkCallback;
+import static android.net.cts.util.CtsTetheringUtils.StartTetheringCallback;
+import static android.net.cts.util.CtsTetheringUtils.TestTetheringEventCallback;
+import static android.net.cts.util.CtsTetheringUtils.isWifiTetheringSupported;
 import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
 import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
 import static android.system.OsConstants.AF_INET;
@@ -90,6 +95,7 @@
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
+import android.net.ConnectivitySettingsManager;
 import android.net.InetAddresses;
 import android.net.IpSecManager;
 import android.net.IpSecManager.UdpEncapsulationSocket;
@@ -106,6 +112,7 @@
 import android.net.SocketKeepalive;
 import android.net.TestNetworkInterface;
 import android.net.TestNetworkManager;
+import android.net.TetheringManager;
 import android.net.cts.util.CtsNetUtils;
 import android.net.util.KeepaliveUtils;
 import android.net.wifi.WifiManager;
@@ -220,6 +227,9 @@
     private static final LinkAddress TEST_LINKADDR = new LinkAddress(
             InetAddresses.parseNumericAddress("2001:db8::8"), 64);
 
+    private static final int AIRPLANE_MODE_OFF = 0;
+    private static final int AIRPLANE_MODE_ON = 1;
+
     private Context mContext;
     private Instrumentation mInstrumentation;
     private ConnectivityManager mCm;
@@ -229,6 +239,7 @@
     private final ArraySet<Integer> mNetworkTypes = new ArraySet<>();
     private UiAutomation mUiAutomation;
     private CtsNetUtils mCtsNetUtils;
+    private TetheringManager mTm;
 
     // Used for cleanup purposes.
     private final List<Range<Integer>> mVpnRequiredUidRanges = new ArrayList<>();
@@ -242,6 +253,7 @@
         mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
         mPackageManager = mContext.getPackageManager();
         mCtsNetUtils = new CtsNetUtils(mContext);
+        mTm = mContext.getSystemService(TetheringManager.class);
 
         if (DevSdkIgnoreRuleKt.isDevSdkInRange(null /* minExclusive */,
                 Build.VERSION_CODES.R /* maxInclusive */)) {
@@ -1922,4 +1934,76 @@
         assertThrows(SecurityException.class, () -> mCm.setGlobalProxy(
                 ProxyInfo.buildDirectProxy("example.com" /* host */, 8080 /* port */)));
     }
+
+    @Test
+    public void testFactoryResetWithoutPermission() {
+        assumeTrue(TestUtils.shouldTestSApis());
+        assertThrows(SecurityException.class, () -> mCm.factoryReset());
+    }
+
+    @Test
+    public void testFactoryReset() throws Exception {
+        assumeTrue(TestUtils.shouldTestSApis());
+
+        // Store current settings.
+        final int curAvoidBadWifi =
+                ConnectivitySettingsManager.getNetworkAvoidBadWifi(mContext);
+        final int curPrivateDnsMode = ConnectivitySettingsManager.getPrivateDnsMode(mContext);
+
+        final TestTetheringEventCallback tetherEventCallback = new TestTetheringEventCallback();
+        try {
+            mTm.registerTetheringEventCallback(c -> c.run() /* executor */, tetherEventCallback);
+            // Adopt for NETWORK_SETTINGS permission.
+            mUiAutomation.adoptShellPermissionIdentity();
+            // start tethering
+            tetherEventCallback.assumeWifiTetheringSupported(mContext);
+            startWifiTethering(tetherEventCallback);
+            // Update setting to verify the behavior.
+            mCm.setAirplaneMode(true);
+            ConnectivitySettingsManager.setPrivateDnsMode(mContext,
+                    ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF);
+            ConnectivitySettingsManager.setNetworkAvoidBadWifi(mContext,
+                    ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI_IGNORE);
+            assertEquals(AIRPLANE_MODE_ON, Settings.Global.getInt(
+                    mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON));
+            // Verify factoryReset
+            mCm.factoryReset();
+            verifySettings(AIRPLANE_MODE_OFF,
+                    ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC,
+                    ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI_PROMPT);
+
+            tetherEventCallback.expectNoTetheringActive();
+        } finally {
+            // Restore settings.
+            mCm.setAirplaneMode(false);
+            ConnectivitySettingsManager.setNetworkAvoidBadWifi(mContext, curAvoidBadWifi);
+            ConnectivitySettingsManager.setPrivateDnsMode(mContext, curPrivateDnsMode);
+            mTm.unregisterTetheringEventCallback(tetherEventCallback);
+            mTm.stopAllTethering();
+            mUiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
+    private void verifySettings(int expectedAirplaneMode, int expectedPrivateDnsMode,
+            int expectedAvoidBadWifi) throws Exception {
+        assertEquals(expectedAirplaneMode, Settings.Global.getInt(
+                mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON));
+        assertEquals(expectedPrivateDnsMode,
+                ConnectivitySettingsManager.getPrivateDnsMode(mContext));
+        assertEquals(expectedAvoidBadWifi,
+                ConnectivitySettingsManager.getNetworkAvoidBadWifi(mContext));
+    }
+
+    private void startWifiTethering(final TestTetheringEventCallback callback) throws Exception {
+        if (!isWifiTetheringSupported(mContext, callback)) return;
+
+        final List<String> wifiRegexs =
+                callback.getTetheringInterfaceRegexps().getTetherableWifiRegexs();
+        final StartTetheringCallback startTetheringCallback = new StartTetheringCallback();
+        final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI)
+                .setShouldShowEntitlementUi(false).build();
+        mTm.startTethering(request, c -> c.run() /* executor */, startTetheringCallback);
+        startTetheringCallback.verifyTetheringStarted();
+        callback.expectTetheredInterfacesChanged(wifiRegexs, TETHERING_WIFI);
+    }
 }
diff --git a/tests/cts/net/src/android/net/cts/NetworkRequestTest.java b/tests/cts/net/src/android/net/cts/NetworkRequestTest.java
index 8c35b97..63863da 100644
--- a/tests/cts/net/src/android/net/cts/NetworkRequestTest.java
+++ b/tests/cts/net/src/android/net/cts/NetworkRequestTest.java
@@ -30,6 +30,7 @@
 
 import static junit.framework.Assert.fail;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
@@ -418,6 +419,21 @@
         }
     }
 
+    @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+    public void testGetCapabilities() {
+        final int[] netCapabilities = new int[] {
+                NET_CAPABILITY_INTERNET,
+                NET_CAPABILITY_NOT_ROAMING };
+        final NetworkCapabilities.Builder builder = NetworkCapabilities.Builder
+                .withoutDefaultCapabilities();
+        for (int capability : netCapabilities) builder.addCapability(capability);
+        final NetworkRequest nr = new NetworkRequest.Builder()
+                .clearCapabilities()
+                .setCapabilities(builder.build())
+                .build();
+        assertArrayEquals(netCapabilities, nr.getCapabilities());
+    }
+
     @Test
     public void testBuildRequestFromExistingRequestWithBuilder() {
         assumeTrue(TestUtils.shouldTestSApis());
diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
index c95dc28..c220326 100644
--- a/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
@@ -22,7 +22,7 @@
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
 
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -35,6 +35,7 @@
 import android.content.pm.PackageManager;
 import android.net.Network;
 import android.net.TetheredClient;
+import android.net.TetheringInterface;
 import android.net.TetheringManager;
 import android.net.TetheringManager.TetheringEventCallback;
 import android.net.TetheringManager.TetheringInterfaceRegexps;
@@ -51,6 +52,7 @@
 
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 
 public final class CtsTetheringUtils {
     private TetheringManager mTm;
@@ -116,23 +118,38 @@
         }
     }
 
-    public static boolean isIfaceMatch(final List<String> ifaceRegexs, final List<String> ifaces) {
-        return isIfaceMatch(ifaceRegexs.toArray(new String[0]), ifaces);
-    }
-
-    public static boolean isIfaceMatch(final String[] ifaceRegexs, final List<String> ifaces) {
+    private static boolean isRegexMatch(final String[] ifaceRegexs, String iface) {
         if (ifaceRegexs == null) fail("ifaceRegexs should not be null");
 
+        for (String regex : ifaceRegexs) {
+            if (iface.matches(regex)) return true;
+        }
+
+        return false;
+    }
+
+    public static boolean isAnyIfaceMatch(final String[] ifaceRegexs, final List<String> ifaces) {
         if (ifaces == null) return false;
 
         for (String s : ifaces) {
-            for (String regex : ifaceRegexs) {
-                if (s.matches(regex)) {
-                    return true;
-                }
+            if (isRegexMatch(ifaceRegexs, s)) return true;
+        }
+
+        return false;
+    }
+
+    private static TetheringInterface getFirstMatchingTetheringInterface(final List<String> regexs,
+            final int type, final Set<TetheringInterface> ifaces) {
+        if (ifaces == null || regexs == null) return null;
+
+        final String[] regexArray = regexs.toArray(new String[0]);
+        for (TetheringInterface iface : ifaces) {
+            if (isRegexMatch(regexArray, iface.getInterface()) && type == iface.getType()) {
+                return iface;
             }
         }
-        return false;
+
+        return null;
     }
 
     // Must poll the callback before looking at the member.
@@ -171,6 +188,8 @@
         private TetheringInterfaceRegexps mTetherableRegex;
         private List<String> mTetherableIfaces;
         private List<String> mTetheredIfaces;
+        private String mErrorIface;
+        private int mErrorCode;
 
         @Override
         public void onTetheringSupported(boolean supported) {
@@ -191,17 +210,41 @@
         @Override
         public void onTetherableInterfacesChanged(List<String> interfaces) {
             mTetherableIfaces = interfaces;
+        }
+        // Call the interface default implementation, which will call
+        // onTetherableInterfacesChanged(List<String>). This ensures that the default implementation
+        // of the new callback method calls the old callback method and avoids the need to convert
+        // Set<TetheringInterface> to List<String> in this code.
+        @Override
+        public void onTetherableInterfacesChanged(Set<TetheringInterface> interfaces) {
+            TetheringEventCallback.super.onTetherableInterfacesChanged(interfaces);
+            assertHasAllTetheringInterfaces(interfaces, mTetherableIfaces);
             mHistory.add(new CallbackValue(CallbackType.ON_TETHERABLE_IFACES, interfaces, 0));
         }
 
         @Override
         public void onTetheredInterfacesChanged(List<String> interfaces) {
             mTetheredIfaces = interfaces;
+        }
+
+        @Override
+        public void onTetheredInterfacesChanged(Set<TetheringInterface> interfaces) {
+            TetheringEventCallback.super.onTetheredInterfacesChanged(interfaces);
+            assertHasAllTetheringInterfaces(interfaces, mTetheredIfaces);
             mHistory.add(new CallbackValue(CallbackType.ON_TETHERED_IFACES, interfaces, 0));
         }
 
         @Override
         public void onError(String ifName, int error) {
+            mErrorIface = ifName;
+            mErrorCode = error;
+        }
+
+        @Override
+        public void onError(TetheringInterface ifName, int error) {
+            TetheringEventCallback.super.onError(ifName, error);
+            assertEquals(ifName.getInterface(), mErrorIface);
+            assertEquals(error, mErrorCode);
             mHistory.add(new CallbackValue(CallbackType.ON_ERROR, ifName, error));
         }
 
@@ -215,30 +258,66 @@
             mHistory.add(new CallbackValue(CallbackType.ON_OFFLOAD_STATUS, status, 0));
         }
 
-        public void expectTetherableInterfacesChanged(@NonNull List<String> regexs) {
+        private void assertHasAllTetheringInterfaces(Set<TetheringInterface> tetheringIfaces,
+                List<String> ifaces) {
+            // This does not check that the interfaces are the same. This checks that the
+            // List<String> has all the interface names contained by the Set<TetheringInterface>.
+            assertEquals(tetheringIfaces.size(), ifaces.size());
+            for (TetheringInterface tether : tetheringIfaces) {
+                assertTrue("iface " + tether.getInterface()
+                        + " seen by new callback but not old callback",
+                        ifaces.contains(tether.getInterface()));
+            }
+        }
+
+        public void expectTetherableInterfacesChanged(@NonNull final List<String> regexs,
+                final int type) {
             assertNotNull("No expected tetherable ifaces callback", mCurrent.poll(TIMEOUT_MS,
                 (cv) -> {
                     if (cv.callbackType != CallbackType.ON_TETHERABLE_IFACES) return false;
-                    final List<String> interfaces = (List<String>) cv.callbackParam;
-                    return isIfaceMatch(regexs, interfaces);
+                    final Set<TetheringInterface> interfaces =
+                            (Set<TetheringInterface>) cv.callbackParam;
+                    return getFirstMatchingTetheringInterface(regexs, type, interfaces) != null;
                 }));
         }
 
-        public void expectTetheredInterfacesChanged(@NonNull List<String> regexs) {
-            assertNotNull("No expected tethered ifaces callback", mCurrent.poll(TIMEOUT_MS,
-                (cv) -> {
-                    if (cv.callbackType != CallbackType.ON_TETHERED_IFACES) return false;
+        public void expectNoTetheringActive() {
+            assertNotNull("At least one tethering type unexpectedly active",
+                    mCurrent.poll(TIMEOUT_MS, (cv) -> {
+                        if (cv.callbackType != CallbackType.ON_TETHERED_IFACES) return false;
 
-                    final List<String> interfaces = (List<String>) cv.callbackParam;
+                        return ((Set<TetheringInterface>) cv.callbackParam).isEmpty();
+                    }));
+        }
 
-                    // Null regexs means no active tethering.
-                    if (regexs == null) return interfaces.isEmpty();
+        public TetheringInterface expectTetheredInterfacesChanged(
+                @NonNull final List<String> regexs, final int type) {
+            while (true) {
+                final CallbackValue cv = mCurrent.poll(TIMEOUT_MS, c -> true);
+                if (cv == null) {
+                    fail("No expected tethered ifaces callback, expected type: " + type);
+                }
 
-                    return isIfaceMatch(regexs, interfaces);
-                }));
+                if (cv.callbackType != CallbackType.ON_TETHERED_IFACES) continue;
+
+                final Set<TetheringInterface> interfaces =
+                        (Set<TetheringInterface>) cv.callbackParam;
+
+                final TetheringInterface iface =
+                        getFirstMatchingTetheringInterface(regexs, type, interfaces);
+
+                if (iface != null) return iface;
+            }
         }
 
         public void expectCallbackStarted() {
+            // This method uses its own readhead because it just check whether last tethering status
+            // is updated after TetheringEventCallback get registered but do not check content
+            // of received callbacks. Using shared readhead (mCurrent) only when the callbacks the
+            // method polled is also not necessary for other methods which using shared readhead.
+            // All of methods using mCurrent is order mattered.
+            final ArrayTrackRecord<CallbackValue>.ReadHead history =
+                    mHistory.newReadHead();
             int receivedBitMap = 0;
             // The each bit represent a type from CallbackType.ON_*.
             // Expect all of callbacks except for ON_ERROR.
@@ -246,7 +325,7 @@
             // Receive ON_ERROR on started callback is not matter. It just means tethering is
             // failed last time, should able to continue the test this time.
             while ((receivedBitMap & expectedBitMap) != expectedBitMap) {
-                final CallbackValue cv = mCurrent.poll(TIMEOUT_MS, c -> true);
+                final CallbackValue cv = history.poll(TIMEOUT_MS, c -> true);
                 if (cv == null) {
                     fail("No expected callbacks, " + "expected bitmap: "
                             + expectedBitMap + ", actual: " + receivedBitMap);
@@ -269,14 +348,14 @@
             }));
         }
 
-        public void expectErrorOrTethered(final String iface) {
+        public void expectErrorOrTethered(final TetheringInterface iface) {
             assertNotNull("No expected callback", mCurrent.poll(TIMEOUT_MS, (cv) -> {
                 if (cv.callbackType == CallbackType.ON_ERROR
-                        && iface.equals((String) cv.callbackParam)) {
+                        && iface.equals((TetheringInterface) cv.callbackParam)) {
                     return true;
                 }
                 if (cv.callbackType == CallbackType.ON_TETHERED_IFACES
-                        && ((List<String>) cv.callbackParam).contains(iface)) {
+                        && ((Set<TetheringInterface>) cv.callbackParam).contains(iface)) {
                     return true;
                 }
 
@@ -309,33 +388,12 @@
             assumeTetheringSupported();
 
             assumeTrue(!getTetheringInterfaceRegexps().getTetherableWifiRegexs().isEmpty());
-
-            final PackageManager pm = ctx.getPackageManager();
-            assumeTrue(pm.hasSystemFeature(PackageManager.FEATURE_WIFI));
-
-            WifiManager wm = ctx.getSystemService(WifiManager.class);
-            // Wifi feature flags only work when wifi is on.
-            final boolean previousWifiEnabledState = wm.isWifiEnabled();
-            try {
-                if (!previousWifiEnabledState) SystemUtil.runShellCommand("svc wifi enable");
-                waitForWifiEnabled(ctx);
-                assumeTrue(wm.isPortableHotspotSupported());
-            } finally {
-                if (!previousWifiEnabledState) SystemUtil.runShellCommand("svc wifi disable");
-            }
+            assumeTrue(isPortableHotspotSupported(ctx));
         }
 
         public TetheringInterfaceRegexps getTetheringInterfaceRegexps() {
             return mTetherableRegex;
         }
-
-        public List<String> getTetherableInterfaces() {
-            return mTetherableIfaces;
-        }
-
-        public List<String> getTetheredInterfaces() {
-            return mTetheredIfaces;
-        }
     }
 
     private static void waitForWifiEnabled(final Context ctx) throws Exception {
@@ -382,14 +440,31 @@
         return callback.getTetheringInterfaceRegexps().getTetherableWifiRegexs();
     }
 
-    public static boolean isWifiTetheringSupported(final TestTetheringEventCallback callback) {
-        return !getWifiTetherableInterfaceRegexps(callback).isEmpty();
+    public static boolean isWifiTetheringSupported(final Context ctx,
+            final TestTetheringEventCallback callback) throws Exception {
+        return !getWifiTetherableInterfaceRegexps(callback).isEmpty()
+                && isPortableHotspotSupported(ctx);
     }
 
-    public void startWifiTethering(final TestTetheringEventCallback callback)
+    /* Returns if wifi supports hotspot. */
+    private static boolean isPortableHotspotSupported(final Context ctx) throws Exception {
+        final PackageManager pm = ctx.getPackageManager();
+        if (!pm.hasSystemFeature(PackageManager.FEATURE_WIFI)) return false;
+        final WifiManager wm = ctx.getSystemService(WifiManager.class);
+        // Wifi feature flags only work when wifi is on.
+        final boolean previousWifiEnabledState = wm.isWifiEnabled();
+        try {
+            if (!previousWifiEnabledState) SystemUtil.runShellCommand("svc wifi enable");
+            waitForWifiEnabled(ctx);
+            return wm.isPortableHotspotSupported();
+        } finally {
+            if (!previousWifiEnabledState) SystemUtil.runShellCommand("svc wifi disable");
+        }
+    }
+
+    public TetheringInterface startWifiTethering(final TestTetheringEventCallback callback)
             throws InterruptedException {
         final List<String> wifiRegexs = getWifiTetherableInterfaceRegexps(callback);
-        assertFalse(isIfaceMatch(wifiRegexs, callback.getTetheredInterfaces()));
 
         final StartTetheringCallback startTetheringCallback = new StartTetheringCallback();
         final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI)
@@ -397,11 +472,14 @@
         mTm.startTethering(request, c -> c.run() /* executor */, startTetheringCallback);
         startTetheringCallback.verifyTetheringStarted();
 
-        callback.expectTetheredInterfacesChanged(wifiRegexs);
+        final TetheringInterface iface =
+                callback.expectTetheredInterfacesChanged(wifiRegexs, TETHERING_WIFI);
 
         callback.expectOneOfOffloadStatusChanged(
                 TETHER_HARDWARE_OFFLOAD_STARTED,
                 TETHER_HARDWARE_OFFLOAD_FAILED);
+
+        return iface;
     }
 
     private static class StopSoftApCallback implements SoftApCallback {
@@ -441,7 +519,7 @@
     public void stopWifiTethering(final TestTetheringEventCallback callback) {
         mTm.stopTethering(TETHERING_WIFI);
         expectSoftApDisabled();
-        callback.expectTetheredInterfacesChanged(null);
+        callback.expectNoTetheringActive();
         callback.expectOneOfOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
     }
 }
diff --git a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
index 71a81ff..0a5e506 100644
--- a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
+++ b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
@@ -26,8 +26,7 @@
 import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
 import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
-import static android.net.cts.util.CtsTetheringUtils.isIfaceMatch;
-import static android.net.cts.util.CtsTetheringUtils.isWifiTetheringSupported;
+import static android.net.cts.util.CtsTetheringUtils.isAnyIfaceMatch;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -48,6 +47,7 @@
 import android.net.LinkAddress;
 import android.net.Network;
 import android.net.NetworkCapabilities;
+import android.net.TetheringInterface;
 import android.net.TetheringManager;
 import android.net.TetheringManager.OnTetheringEntitlementResultListener;
 import android.net.TetheringManager.TetheringInterfaceRegexps;
@@ -190,13 +190,13 @@
         }
 
         private boolean isIfaceActive(final String[] ifaceRegexs, final TetherState state) {
-            return isIfaceMatch(ifaceRegexs, state.mActive);
+            return isAnyIfaceMatch(ifaceRegexs, state.mActive);
         }
 
         private void assertNoErroredIfaces(final TetherState state, final String[] ifaceRegexs) {
             if (state == null || state.mErrored == null) return;
 
-            if (isIfaceMatch(ifaceRegexs, state.mErrored)) {
+            if (isAnyIfaceMatch(ifaceRegexs, state.mErrored)) {
                 fail("Found failed tethering interfaces: " + Arrays.toString(state.mErrored.toArray()));
             }
         }
@@ -256,12 +256,13 @@
 
         try {
             tetherEventCallback.assumeWifiTetheringSupported(mContext);
+            tetherEventCallback.expectNoTetheringActive();
 
-            mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
+            final TetheringInterface tetheredIface =
+                    mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
 
-            final List<String> tetheredIfaces = tetherEventCallback.getTetheredInterfaces();
-            assertEquals(1, tetheredIfaces.size());
-            final String wifiTetheringIface = tetheredIfaces.get(0);
+            assertNotNull(tetheredIface);
+            final String wifiTetheringIface = tetheredIface.getInterface();
 
             mCtsTetheringUtils.stopWifiTethering(tetherEventCallback);
 
@@ -272,7 +273,8 @@
                 if (ret == TETHER_ERROR_NO_ERROR) {
                     // If calling #tether successful, there is a callback to tell the result of
                     // tethering setup.
-                    tetherEventCallback.expectErrorOrTethered(wifiTetheringIface);
+                    tetherEventCallback.expectErrorOrTethered(
+                            new TetheringInterface(TETHERING_WIFI, wifiTetheringIface));
                 }
             } finally {
                 mTM.untether(wifiTetheringIface);
@@ -319,7 +321,7 @@
             mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
 
             mTM.stopAllTethering();
-            tetherEventCallback.expectTetheredInterfacesChanged(null);
+            tetherEventCallback.expectNoTetheringActive();
         } finally {
             mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
         }
@@ -417,6 +419,7 @@
 
         try {
             tetherEventCallback.assumeWifiTetheringSupported(mContext);
+            tetherEventCallback.expectNoTetheringActive();
 
             previousWifiEnabledState = mWm.isWifiEnabled();
             if (previousWifiEnabledState) {
diff --git a/tests/integration/src/android/net/TestNetworkStackClient.kt b/tests/integration/src/android/net/TestNetworkStackClient.kt
index 01eb514..61ef5bd 100644
--- a/tests/integration/src/android/net/TestNetworkStackClient.kt
+++ b/tests/integration/src/android/net/TestNetworkStackClient.kt
@@ -19,6 +19,7 @@
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
+import android.net.networkstack.NetworkStackClientBase
 import android.os.IBinder
 import com.android.server.net.integrationtests.TestNetworkStackService
 import org.mockito.Mockito.any
@@ -29,28 +30,22 @@
 
 const val TEST_ACTION_SUFFIX = ".Test"
 
-class TestNetworkStackClient(context: Context) : NetworkStackClient(TestDependencies(context)) {
+class TestNetworkStackClient(private val context: Context) : NetworkStackClientBase() {
     // TODO: consider switching to TrackRecord for more expressive checks
     private val lastCallbacks = HashMap<Network, INetworkMonitorCallbacks>()
+    private val moduleConnector = ConnectivityModuleConnector { _, action, _, _ ->
+        val intent = Intent(action)
+        val serviceName = TestNetworkStackService::class.qualifiedName
+                ?: fail("TestNetworkStackService name not found")
+        intent.component = ComponentName(context.packageName, serviceName)
+        return@ConnectivityModuleConnector intent
+    }.also { it.init(context) }
 
-    private class TestDependencies(private val context: Context) : Dependencies {
-        override fun addToServiceManager(service: IBinder) = Unit
-        override fun checkCallerUid() = Unit
-
-        override fun getConnectivityModuleConnector(): ConnectivityModuleConnector {
-            return ConnectivityModuleConnector { _, _, _, inSystemProcess ->
-                getNetworkStackIntent(inSystemProcess)
-            }.also { it.init(context) }
-        }
-
-        private fun getNetworkStackIntent(inSystemProcess: Boolean): Intent? {
-            // Simulate out-of-system-process config: in-process service not found (null intent)
-            if (inSystemProcess) return null
-            val intent = Intent(INetworkStackConnector::class.qualifiedName + TEST_ACTION_SUFFIX)
-            val serviceName = TestNetworkStackService::class.qualifiedName
-                    ?: fail("TestNetworkStackService name not found")
-            intent.component = ComponentName(context.packageName, serviceName)
-            return intent
+    fun start() {
+        moduleConnector.startModuleService(
+                INetworkStackConnector::class.qualifiedName + TEST_ACTION_SUFFIX,
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) { connector ->
+            onNetworkStackConnected(INetworkStackConnector.Stub.asInterface(connector))
         }
     }
 
diff --git a/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index b6e4274..e039ef0 100644
--- a/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -157,7 +157,6 @@
         doReturn(IntArray(0)).`when`(systemConfigManager).getSystemPermissionUids(anyString())
 
         networkStackClient = TestNetworkStackClient(realContext)
-        networkStackClient.init()
         networkStackClient.start()
 
         service = TestConnectivityService(makeDependencies())
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 1b4f836..04e63fc 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -228,7 +228,6 @@
 import android.net.NetworkScore;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
-import android.net.NetworkStackClient;
 import android.net.NetworkStateSnapshot;
 import android.net.NetworkTestResultParcelable;
 import android.net.OemNetworkPreferences;
@@ -248,6 +247,7 @@
 import android.net.VpnManager;
 import android.net.VpnTransportInfo;
 import android.net.metrics.IpConnectivityLog;
+import android.net.networkstack.NetworkStackClientBase;
 import android.net.resolv.aidl.Nat64PrefixEventParcel;
 import android.net.resolv.aidl.PrivateDnsValidationEventParcel;
 import android.net.shared.NetworkMonitorUtils;
@@ -461,7 +461,7 @@
     @Mock NetworkStatsManager mStatsManager;
     @Mock IDnsResolver mMockDnsResolver;
     @Mock INetd mMockNetd;
-    @Mock NetworkStackClient mNetworkStack;
+    @Mock NetworkStackClientBase mNetworkStack;
     @Mock PackageManager mPackageManager;
     @Mock UserManager mUserManager;
     @Mock NotificationManager mNotificationManager;
diff --git a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
index c75618f..c6e7606 100644
--- a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -30,7 +30,7 @@
 import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_REQUIRED;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
-import static android.net.ConnectivitySettingsManager.APPS_ALLOWED_ON_RESTRICTED_NETWORKS;
+import static android.net.ConnectivitySettingsManager.UIDS_ALLOWED_ON_RESTRICTED_NETWORKS;
 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
 import static android.os.Process.SYSTEM_UID;
 
@@ -142,7 +142,7 @@
         final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mContext));
         doReturn(UserHandle.ALL).when(asUserCtx).getUser();
         when(mContext.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx);
-        when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>());
+        when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>());
 
         mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps));
 
@@ -341,9 +341,9 @@
     }
 
     @Test
-    public void testHasRestrictedNetworkPermissionAppAllowedOnRestrictedNetworks() {
-        mPermissionMonitor.updateAppsAllowedOnRestrictedNetworks(
-                new ArraySet<>(new String[] { MOCK_PACKAGE1 }));
+    public void testHasRestrictedNetworkPermissionUidAllowedOnRestrictedNetworks() {
+        mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(
+                new ArraySet<>(new Integer[] { MOCK_UID1 }));
         assertTrue(hasRestrictedNetworkPermission(
                 PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1));
         assertTrue(hasRestrictedNetworkPermission(
@@ -352,11 +352,11 @@
                 PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1, CONNECTIVITY_INTERNAL));
 
         assertFalse(hasRestrictedNetworkPermission(
-                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID1));
+                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID2));
         assertFalse(hasRestrictedNetworkPermission(
-                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID1, CHANGE_NETWORK_STATE));
+                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID2, CHANGE_NETWORK_STATE));
         assertFalse(hasRestrictedNetworkPermission(
-                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID1, CONNECTIVITY_INTERNAL));
+                PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID2, CONNECTIVITY_INTERNAL));
 
     }
 
@@ -396,32 +396,32 @@
         assertFalse(wouldBeCarryoverPackage(PARTITION_PRODUCT, VERSION_Q, MOCK_UID1));
     }
 
-    private boolean wouldBeAppAllowedOnRestrictedNetworks(String packageName) {
-        final PackageInfo packageInfo = new PackageInfo();
-        packageInfo.packageName = packageName;
-        return mPermissionMonitor.isAppAllowedOnRestrictedNetworks(packageInfo);
+    private boolean wouldBeUidAllowedOnRestrictedNetworks(int uid) {
+        final ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.uid = uid;
+        return mPermissionMonitor.isUidAllowedOnRestrictedNetworks(applicationInfo);
     }
 
     @Test
     public void testIsAppAllowedOnRestrictedNetworks() {
-        mPermissionMonitor.updateAppsAllowedOnRestrictedNetworks(new ArraySet<>());
-        assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE1));
-        assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE2));
+        mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(new ArraySet<>());
+        assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID1));
+        assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID2));
 
-        mPermissionMonitor.updateAppsAllowedOnRestrictedNetworks(
-                new ArraySet<>(new String[] { MOCK_PACKAGE1 }));
-        assertTrue(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE1));
-        assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE2));
+        mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(
+                new ArraySet<>(new Integer[] { MOCK_UID1 }));
+        assertTrue(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID1));
+        assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID2));
 
-        mPermissionMonitor.updateAppsAllowedOnRestrictedNetworks(
-                new ArraySet<>(new String[] { MOCK_PACKAGE2 }));
-        assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE1));
-        assertTrue(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE2));
+        mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(
+                new ArraySet<>(new Integer[] { MOCK_UID2 }));
+        assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID1));
+        assertTrue(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID2));
 
-        mPermissionMonitor.updateAppsAllowedOnRestrictedNetworks(
-                new ArraySet<>(new String[] { "com.android.test" }));
-        assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE1));
-        assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE2));
+        mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(
+                new ArraySet<>(new Integer[] { 123 }));
+        assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID1));
+        assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID2));
     }
 
     private void assertBackgroundPermission(boolean hasPermission, String name, int uid,
@@ -901,12 +901,12 @@
     }
 
     @Test
-    public void testAppsAllowedOnRestrictedNetworksChanged() throws Exception {
+    public void testUidsAllowedOnRestrictedNetworksChanged() throws Exception {
         final NetdMonitor mNetdMonitor = new NetdMonitor(mNetdService);
         final ArgumentCaptor<ContentObserver> captor =
                 ArgumentCaptor.forClass(ContentObserver.class);
         verify(mDeps, times(1)).registerContentObserver(any(),
-                argThat(uri -> uri.getEncodedPath().contains(APPS_ALLOWED_ON_RESTRICTED_NETWORKS)),
+                argThat(uri -> uri.getEncodedPath().contains(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS)),
                 anyBoolean(), captor.capture());
         final ContentObserver contentObserver = captor.getValue();
 
@@ -924,24 +924,24 @@
         when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE2), anyInt())).thenReturn(packageInfo2);
         when(mPackageManager.getPackagesForUid(MOCK_UID2)).thenReturn(new String[]{MOCK_PACKAGE2});
 
-        // MOCK_PACKAGE1 is listed in setting that allow to use restricted networks, MOCK_UID1
+        // MOCK_UID1 is listed in setting that allow to use restricted networks, MOCK_UID1
         // should have SYSTEM permission.
-        when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(
-                new ArraySet<>(new String[] { MOCK_PACKAGE1 }));
+        when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(
+                new ArraySet<>(new Integer[] { MOCK_UID1 }));
         contentObserver.onChange(true /* selfChange */);
         mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
         mNetdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID2});
 
-        // MOCK_PACKAGE2 is listed in setting that allow to use restricted networks, MOCK_UID2
+        // 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.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(
-                new ArraySet<>(new String[] { MOCK_PACKAGE2 }));
+        when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(
+                new ArraySet<>(new Integer[] { MOCK_UID2 }));
         contentObserver.onChange(true /* selfChange */);
         mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID2});
         mNetdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
 
-        // No app lists in setting, should revoke permission from all uids.
-        when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>());
+        // No uid lists in setting, should revoke permission from all uids.
+        when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>());
         contentObserver.onChange(true /* selfChange */);
         mNetdMonitor.expectNoPermission(
                 new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1, MOCK_UID2});
@@ -953,7 +953,7 @@
         final ArgumentCaptor<ContentObserver> captor =
                 ArgumentCaptor.forClass(ContentObserver.class);
         verify(mDeps, times(1)).registerContentObserver(any(),
-                argThat(uri -> uri.getEncodedPath().contains(APPS_ALLOWED_ON_RESTRICTED_NETWORKS)),
+                argThat(uri -> uri.getEncodedPath().contains(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS)),
                 anyBoolean(), captor.capture());
         final ContentObserver contentObserver = captor.getValue();
 
@@ -974,22 +974,15 @@
         addPackageForUsers(new UserHandle[]{MOCK_USER1}, MOCK_PACKAGE1, MOCK_UID1);
         mNetdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
 
-        // MOCK_PACKAGE2 is listed in setting that allow to use restricted networks, MOCK_UID1
+        // MOCK_UID1 is listed in setting that allow to use restricted networks, MOCK_UID1
         // should upgrade to SYSTEM permission.
-        when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(
-                new ArraySet<>(new String[] { MOCK_PACKAGE2 }));
-        contentObserver.onChange(true /* selfChange */);
-        mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
-
-        // MOCK_PACKAGE1 is listed in setting that allow to use restricted networks, MOCK_UID1
-        // should still have SYSTEM permission.
-        when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(
-                new ArraySet<>(new String[] { MOCK_PACKAGE1 }));
+        when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(
+                new ArraySet<>(new Integer[] { MOCK_UID1 }));
         contentObserver.onChange(true /* selfChange */);
         mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
 
         // No app lists in setting, MOCK_UID1 should downgrade to NETWORK permission.
-        when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>());
+        when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>());
         contentObserver.onChange(true /* selfChange */);
         mNetdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
 
@@ -998,4 +991,4 @@
         removePackageForUsers(new UserHandle[]{MOCK_USER1}, MOCK_PACKAGE1, MOCK_UID1);
         mNetdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
     }
-}
\ No newline at end of file
+}