Merge "Remove MessageUtils usage in VpnTransportInfo"
diff --git a/core/java/android/net/NetworkScore.java b/core/java/android/net/NetworkScore.java
new file mode 100644
index 0000000..f478010
--- /dev/null
+++ b/core/java/android/net/NetworkScore.java
@@ -0,0 +1,108 @@
+/*
+ * 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.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Object representing the quality of a network as perceived by the user.
+ *
+ * A NetworkScore object represents the characteristics of a network that affects how good the
+ * network is considered for a particular use.
+ * @hide
+ */
+// TODO : @SystemApi when the implementation is complete
+public final class NetworkScore implements Parcelable {
+    // This will be removed soon. Do *NOT* depend on it for any new code that is not part of
+    // a migration.
+    private final int mLegacyInt;
+
+    /** @hide */
+    NetworkScore(final int legacyInt) {
+        this.mLegacyInt = legacyInt;
+    }
+
+    private NetworkScore(@NonNull final Parcel in) {
+        mLegacyInt = in.readInt();
+    }
+
+    public int getLegacyInt() {
+        return mLegacyInt;
+    }
+
+    @Override
+    public String toString() {
+        return "Score(" + mLegacyInt + ")";
+    }
+
+    @Override
+    public void writeToParcel(@NonNull final Parcel dest, final int flags) {
+        dest.writeInt(mLegacyInt);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull public static final Creator<NetworkScore> CREATOR = new Creator<>() {
+        @Override
+        @NonNull
+        public NetworkScore createFromParcel(@NonNull final Parcel in) {
+            return new NetworkScore(in);
+        }
+
+        @Override
+        @NonNull
+        public NetworkScore[] newArray(int size) {
+            return new NetworkScore[size];
+        }
+    };
+
+    /**
+     * A builder for NetworkScore.
+     */
+    public static final class Builder {
+        private static final int INVALID_LEGACY_INT = Integer.MIN_VALUE;
+        private int mLegacyInt = INVALID_LEGACY_INT;
+
+        /**
+         * Sets the legacy int for this score.
+         *
+         * Do not rely on this. It will be gone by the time S is released.
+         *
+         * @param score the legacy int
+         * @return this
+         */
+        @NonNull
+        public Builder setLegacyInt(final int score) {
+            mLegacyInt = score;
+            return this;
+        }
+
+        /**
+         * Builds this NetworkScore.
+         * @return The built NetworkScore object.
+         */
+        @NonNull
+        public NetworkScore build() {
+            return new NetworkScore(mLegacyInt);
+        }
+    }
+}
diff --git a/core/jni/android_net_NetworkUtils.cpp b/core/jni/android_net_NetworkUtils.cpp
index 7508108..a781a37 100644
--- a/core/jni/android_net_NetworkUtils.cpp
+++ b/core/jni/android_net_NetworkUtils.cpp
@@ -52,27 +52,6 @@
 // FrameworkListener limits the size of commands to 4096 bytes.
 constexpr int MAXCMDSIZE = 4096;
 
-static void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
-    ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
-    if (detailMessage.get() == NULL) {
-        // Not really much we can do here. We're probably dead in the water,
-        // but let's try to stumble on...
-        env->ExceptionClear();
-    }
-    static jclass errnoExceptionClass =
-            MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/system/ErrnoException"));
-
-    static jmethodID errnoExceptionCtor =
-            GetMethodIDOrDie(env, errnoExceptionClass,
-            "<init>", "(Ljava/lang/String;I)V");
-
-    jobject exception = env->NewObject(errnoExceptionClass,
-                                       errnoExceptionCtor,
-                                       detailMessage.get(),
-                                       error);
-    env->Throw(reinterpret_cast<jthrowable>(exception));
-}
-
 static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
 {
     struct sock_filter filter_code[] = {
@@ -150,7 +129,7 @@
     int fd = resNetworkQuery(netId, queryname.data(), ns_class, ns_type, flags);
 
     if (fd < 0) {
-        throwErrnoException(env, "resNetworkQuery", -fd);
+        jniThrowErrnoException(env, "resNetworkQuery", -fd);
         return nullptr;
     }
 
@@ -165,7 +144,7 @@
     int fd = resNetworkSend(netId, data, msgLen, flags);
 
     if (fd < 0) {
-        throwErrnoException(env, "resNetworkSend", -fd);
+        jniThrowErrnoException(env, "resNetworkSend", -fd);
         return nullptr;
     }
 
@@ -180,13 +159,13 @@
     int res = resNetworkResult(fd, &rcode, buf.data(), MAXPACKETSIZE);
     jniSetFileDescriptorOfFD(env, javaFd, -1);
     if (res < 0) {
-        throwErrnoException(env, "resNetworkResult", -res);
+        jniThrowErrnoException(env, "resNetworkResult", -res);
         return nullptr;
     }
 
     jbyteArray answer = env->NewByteArray(res);
     if (answer == nullptr) {
-        throwErrnoException(env, "resNetworkResult", ENOMEM);
+        jniThrowErrnoException(env, "resNetworkResult", ENOMEM);
         return nullptr;
     } else {
         env->SetByteArrayRegion(answer, 0, res,
@@ -208,7 +187,7 @@
 static jobject android_net_utils_getDnsNetwork(JNIEnv *env, jobject thiz) {
     unsigned dnsNetId = 0;
     if (int res = getNetworkForDns(&dnsNetId) < 0) {
-        throwErrnoException(env, "getDnsNetId", -res);
+        jniThrowErrnoException(env, "getDnsNetId", -res);
         return nullptr;
     }
     bool privateDnsBypass = dnsNetId & NETID_USE_LOCAL_NAMESERVERS;
@@ -233,8 +212,8 @@
     // Obtain the parameters of the TCP repair window.
     int rc = getsockopt(fd, IPPROTO_TCP, TCP_REPAIR_WINDOW, &trw, &size);
     if (rc == -1) {
-      throwErrnoException(env, "getsockopt : TCP_REPAIR_WINDOW", errno);
-      return NULL;
+        jniThrowErrnoException(env, "getsockopt : TCP_REPAIR_WINDOW", errno);
+        return NULL;
     }
 
     struct tcp_info tcpinfo = {};
@@ -244,8 +223,8 @@
     // should be applied to the window size.
     rc = getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcpinfo, &tcpinfo_size);
     if (rc == -1) {
-      throwErrnoException(env, "getsockopt : TCP_INFO", errno);
-      return NULL;
+        jniThrowErrnoException(env, "getsockopt : TCP_INFO", errno);
+        return NULL;
     }
 
     jclass class_TcpRepairWindow = env->FindClass("android/net/TcpRepairWindow");
diff --git a/framework/api/current.txt b/framework/api/current.txt
index a8f1a4d..f22d4b7 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -87,6 +87,7 @@
     method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public boolean isActiveNetworkMetered();
     method public boolean isDefaultNetworkActive();
     method @Deprecated public static boolean isNetworkTypeValid(int);
+    method public void registerBestMatchingNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback);
     method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback);
@@ -143,6 +144,7 @@
 
   public static class ConnectivityManager.NetworkCallback {
     ctor public ConnectivityManager.NetworkCallback();
+    ctor public ConnectivityManager.NetworkCallback(int);
     method public void onAvailable(@NonNull android.net.Network);
     method public void onBlockedStatusChanged(@NonNull android.net.Network, boolean);
     method public void onCapabilitiesChanged(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities);
@@ -150,6 +152,7 @@
     method public void onLosing(@NonNull android.net.Network, int);
     method public void onLost(@NonNull android.net.Network);
     method public void onUnavailable();
+    field public static final int FLAG_INCLUDE_LOCATION_INFO = 1; // 0x1
   }
 
   public static interface ConnectivityManager.OnNetworkActiveListener {
@@ -293,6 +296,7 @@
     method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
     method public int getOwnerUid();
     method public int getSignalStrength();
+    method @NonNull public java.util.Set<java.lang.Integer> getSubIds();
     method @Nullable public android.net.TransportInfo getTransportInfo();
     method public boolean hasCapability(int);
     method public boolean hasTransport(int);
@@ -399,6 +403,11 @@
     method public android.net.NetworkRequest.Builder removeTransportType(int);
     method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String);
     method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
+    method @NonNull public android.net.NetworkRequest.Builder setSubIds(@NonNull java.util.Set<java.lang.Integer>);
+  }
+
+  public class ParseException extends java.lang.RuntimeException {
+    field public String response;
   }
 
   public class ProxyInfo implements android.os.Parcelable {
diff --git a/framework/api/module-lib-current.txt b/framework/api/module-lib-current.txt
index d2ed73e..c3b1800 100644
--- a/framework/api/module-lib-current.txt
+++ b/framework/api/module-lib-current.txt
@@ -7,9 +7,14 @@
 
   public class ConnectivityManager {
     method @NonNull @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public java.util.List<android.net.NetworkStateSnapshot> getAllNetworkStateSnapshot();
+    method @NonNull public static android.util.Range<java.lang.Integer> getIpSecNetIdRange();
+    method @NonNull public static String getPrivateDnsMode(@NonNull android.content.ContentResolver);
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
-    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @Nullable android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
+    field public static final String PRIVATE_DNS_MODE_OFF = "off";
+    field public static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
+    field public static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname";
   }
 
   public final class NetworkAgentConfig implements android.os.Parcelable {
@@ -24,6 +29,11 @@
     field public static final int TRANSPORT_TEST = 7; // 0x7
   }
 
+  public class ParseException extends java.lang.RuntimeException {
+    ctor public ParseException(@NonNull String);
+    ctor public ParseException(@NonNull String, @NonNull Throwable);
+  }
+
   public final class TcpRepairWindow {
     ctor public TcpRepairWindow(int, int, int, int, int, int);
     field public final int maxWindow;
@@ -51,6 +61,14 @@
     field public static final String TEST_TAP_PREFIX = "testtap";
   }
 
+  public final class TestNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
+    ctor public TestNetworkSpecifier(@NonNull String);
+    method public int describeContents();
+    method @Nullable public String getInterfaceName();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.TestNetworkSpecifier> CREATOR;
+  }
+
   public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo {
     ctor public VpnTransportInfo(int);
     method public int describeContents();
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index a732430..a98f14e 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -296,6 +296,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorUid(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkCapabilities.Builder setSignalStrength(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String);
+    method @NonNull public android.net.NetworkCapabilities.Builder setSubIds(@NonNull java.util.Set<java.lang.Integer>);
     method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo);
   }
 
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index 45ed317..d7cae2f 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -16,13 +16,15 @@
 package android.net;
 
 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
-import static android.net.IpSecManager.INVALID_RESOURCE_ID;
 import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST;
 import static android.net.NetworkRequest.Type.LISTEN;
+import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST;
 import static android.net.NetworkRequest.Type.REQUEST;
 import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
 import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT;
 import static android.net.QosCallback.QosCallbackRegistrationException;
+import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE;
+import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
 
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
@@ -31,11 +33,13 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.StringDef;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.app.PendingIntent;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.net.IpSecManager.UdpEncapsulationSocket;
@@ -43,6 +47,7 @@
 import android.net.TetheringManager.StartTetheringCallback;
 import android.net.TetheringManager.TetheringEventCallback;
 import android.net.TetheringManager.TetheringRequest;
+import android.net.wifi.WifiNetworkSuggestion;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
@@ -62,6 +67,7 @@
 import android.provider.Settings;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Range;
@@ -801,24 +807,27 @@
     /**
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public static final String PRIVATE_DNS_MODE_OFF = "off";
     /**
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
     /**
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname";
-    /**
-     * The default Private DNS mode.
-     *
-     * This may change from release to release or may become dependent upon
-     * the capabilities of the underlying platform.
-     *
-     * @hide
-     */
-    public static final String PRIVATE_DNS_DEFAULT_MODE_FALLBACK = PRIVATE_DNS_MODE_OPPORTUNISTIC;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef(value = {
+            PRIVATE_DNS_MODE_OFF,
+            PRIVATE_DNS_MODE_OPPORTUNISTIC,
+            PRIVATE_DNS_MODE_PROVIDER_HOSTNAME,
+    })
+    public @interface PrivateDnsMode {}
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
     private final IConnectivityManager mService;
@@ -1314,7 +1323,7 @@
     }
 
     /**
-     * Returns an array of {@link android.net.NetworkCapabilities} objects, representing
+     * Returns an array of {@link NetworkCapabilities} objects, representing
      * the Networks that applications run by the given user will use by default.
      * @hide
      */
@@ -1394,11 +1403,19 @@
     }
 
     /**
-     * Get the {@link android.net.NetworkCapabilities} for the given {@link Network}.  This
+     * Get the {@link NetworkCapabilities} for the given {@link Network}.  This
      * will return {@code null} if the network is unknown.
      *
+     * This will remove any location sensitive data in {@link TransportInfo} embedded in
+     * {@link NetworkCapabilities#getTransportInfo()}. Some transport info instances like
+     * {@link android.net.wifi.WifiInfo} contain location sensitive information. Retrieving
+     * this location sensitive information (subject to app's location permissions) will be
+     * noted by system. To include any location sensitive data in {@link TransportInfo},
+     * use a {@link NetworkCallback} with
+     * {@link NetworkCallback#FLAG_INCLUDE_LOCATION_INFO} flag.
+     *
      * @param network The {@link Network} object identifying the network in question.
-     * @return The {@link android.net.NetworkCapabilities} for the network, or {@code null}.
+     * @return The {@link NetworkCapabilities} for the network, or {@code null}.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
     @Nullable
@@ -1996,7 +2013,7 @@
             dup = createInvalidFd();
         }
         return new NattSocketKeepalive(mService, network, dup,
-                INVALID_RESOURCE_ID /* Unused */, source, destination, executor, callback);
+                -1 /* Unused */, source, destination, executor, callback);
     }
 
     /**
@@ -3196,10 +3213,6 @@
         }
     }
 
-    // TODO : remove this method. It is a stopgap measure to help sheperding a number
-    // of dependent changes that would conflict throughout the automerger graph. Having this
-    // temporarily helps with the process of going through with all these dependent changes across
-    // the entire tree.
     /**
      * @hide
      * Register a NetworkAgent with ConnectivityService.
@@ -3209,20 +3222,8 @@
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
             android.Manifest.permission.NETWORK_FACTORY})
     public Network registerNetworkAgent(INetworkAgent na, NetworkInfo ni, LinkProperties lp,
-            NetworkCapabilities nc, int score, NetworkAgentConfig config) {
-        return registerNetworkAgent(na, ni, lp, nc, score, config, NetworkProvider.ID_NONE);
-    }
-
-    /**
-     * @hide
-     * Register a NetworkAgent with ConnectivityService.
-     * @return Network corresponding to NetworkAgent.
-     */
-    @RequiresPermission(anyOf = {
-            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
-            android.Manifest.permission.NETWORK_FACTORY})
-    public Network registerNetworkAgent(INetworkAgent na, NetworkInfo ni, LinkProperties lp,
-            NetworkCapabilities nc, int score, NetworkAgentConfig config, int providerId) {
+            NetworkCapabilities nc, @NonNull NetworkScore score, NetworkAgentConfig config,
+            int providerId) {
         try {
             return mService.registerNetworkAgent(na, ni, lp, nc, score, config, providerId);
         } catch (RemoteException e) {
@@ -3244,6 +3245,54 @@
      */
     public static class NetworkCallback {
         /**
+         * No flags associated with this callback.
+         * @hide
+         */
+        public static final int FLAG_NONE = 0;
+        /**
+         * Use this flag to include any location sensitive data in {@link NetworkCapabilities} sent
+         * via {@link #onCapabilitiesChanged(Network, NetworkCapabilities)}.
+         * <p>
+         * These include:
+         * <li> Some transport info instances (retrieved via
+         * {@link NetworkCapabilities#getTransportInfo()}) like {@link android.net.wifi.WifiInfo}
+         * contain location sensitive information.
+         * <li> OwnerUid (retrieved via {@link NetworkCapabilities#getOwnerUid()} is location
+         * sensitive for wifi suggestor apps (i.e using {@link WifiNetworkSuggestion}).</li>
+         * </p>
+         * <p>
+         * Note:
+         * <li> Retrieving this location sensitive information (subject to app's location
+         * permissions) will be noted by system. </li>
+         * <li> Without this flag any {@link NetworkCapabilities} provided via the callback does
+         * not include location sensitive info.
+         * </p>
+         */
+        public static final int FLAG_INCLUDE_LOCATION_INFO = 1 << 0;
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(flag = true, prefix = "FLAG_", value = {
+                FLAG_NONE,
+                FLAG_INCLUDE_LOCATION_INFO
+        })
+        public @interface Flag { }
+
+        /**
+         * All the valid flags for error checking.
+         */
+        private static final int VALID_FLAGS = FLAG_INCLUDE_LOCATION_INFO;
+
+        public NetworkCallback() {
+            this(FLAG_NONE);
+        }
+
+        public NetworkCallback(@Flag int flags) {
+            Preconditions.checkArgument((flags & VALID_FLAGS) == flags);
+            mFlags = flags;
+        }
+
+        /**
          * Called when the framework connects to a new network to evaluate whether it satisfies this
          * request. If evaluation succeeds, this callback may be followed by an {@link #onAvailable}
          * callback. There is no guarantee that this new network will satisfy any requests, or that
@@ -3380,7 +3429,7 @@
          * calling these methods while in a callback may return an outdated or even a null object.
          *
          * @param network The {@link Network} whose capabilities have changed.
-         * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this
+         * @param networkCapabilities The new {@link NetworkCapabilities} for this
          *                            network.
          */
         public void onCapabilitiesChanged(@NonNull Network network,
@@ -3449,6 +3498,7 @@
         public void onBlockedStatusChanged(@NonNull Network network, boolean blocked) {}
 
         private NetworkRequest networkRequest;
+        private final int mFlags;
     }
 
     /**
@@ -3638,14 +3688,15 @@
                 }
                 Messenger messenger = new Messenger(handler);
                 Binder binder = new Binder();
+                final int callbackFlags = callback.mFlags;
                 if (reqType == LISTEN) {
                     request = mService.listenForNetwork(
-                            need, messenger, binder, callingPackageName,
+                            need, messenger, binder, callbackFlags, callingPackageName,
                             getAttributionTag());
                 } else {
                     request = mService.requestNetwork(
                             need, reqType.ordinal(), messenger, timeoutMs, binder, legacyType,
-                            callingPackageName, getAttributionTag());
+                            callbackFlags, callingPackageName, getAttributionTag());
                 }
                 if (request != null) {
                     sCallbacks.put(request, callback);
@@ -3692,7 +3743,7 @@
     }
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
+     * Request a network to satisfy a set of {@link NetworkCapabilities}.
      *
      * <p>This method will attempt to find the best network that matches the passed
      * {@link NetworkRequest}, and to bring up one that does if none currently satisfies the
@@ -3776,7 +3827,7 @@
     }
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
+     * Request a network to satisfy a set of {@link NetworkCapabilities}.
      *
      * This method behaves identically to {@link #requestNetwork(NetworkRequest, NetworkCallback)}
      * but runs all the callbacks on the passed Handler.
@@ -3798,7 +3849,7 @@
     }
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
+     * Request a network to satisfy a set of {@link NetworkCapabilities}, limited
      * by a timeout.
      *
      * This function behaves identically to the non-timed-out version
@@ -3833,7 +3884,7 @@
     }
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
+     * Request a network to satisfy a set of {@link NetworkCapabilities}, limited
      * by a timeout.
      *
      * This method behaves identically to
@@ -3878,7 +3929,7 @@
 
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
+     * Request a network to satisfy a set of {@link NetworkCapabilities}.
      *
      * This function behaves identically to the version that takes a NetworkCallback, but instead
      * of {@link NetworkCallback} a {@link PendingIntent} is used.  This means
@@ -4190,6 +4241,36 @@
     }
 
     /**
+     * Registers to receive notifications about the best matching network which satisfy the given
+     * {@link NetworkRequest}.  The callbacks will continue to be called until
+     * either the application exits or {@link #unregisterNetworkCallback(NetworkCallback)} is
+     * called.
+     *
+     * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
+     * number of outstanding requests to 100 per app (identified by their UID), shared with
+     * {@link #registerNetworkCallback} and its variants and {@link #requestNetwork} as well as
+     * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+     * Requesting a network with this method will count toward this limit. If this limit is
+     * exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources,
+     * make sure to unregister the callbacks with
+     * {@link #unregisterNetworkCallback(NetworkCallback)}.
+     *
+     *
+     * @param request {@link NetworkRequest} describing this request.
+     * @param networkCallback The {@link NetworkCallback} that the system will call as suitable
+     *                        networks change state.
+     * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+     * @throws RuntimeException if the app already has too many callbacks registered.
+     */
+    @SuppressLint("ExecutorRegistration")
+    public void registerBestMatchingNetworkCallback(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
+        final NetworkCapabilities nc = request.networkCapabilities;
+        final CallbackHandler cbHandler = new CallbackHandler(handler);
+        sendRequestForNetwork(nc, networkCallback, 0, LISTEN_FOR_BEST, TYPE_NONE, cbHandler);
+    }
+
+    /**
      * Requests bandwidth update for a given {@link Network} and returns whether the update request
      * is accepted by ConnectivityService. Once accepted, ConnectivityService will poll underlying
      * network connection for updated bandwidth information. The caller will be notified via
@@ -4898,7 +4979,7 @@
     }
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, but
+     * Request a network to satisfy a set of {@link NetworkCapabilities}, but
      * does not cause any networks to retain the NET_CAPABILITY_FOREGROUND capability. This can
      * be used to request that the system provide a network without causing the network to be
      * in the foreground.
@@ -4979,10 +5060,10 @@
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
     })
     public void requestBackgroundNetwork(@NonNull NetworkRequest request,
-            @Nullable Handler handler, @NonNull NetworkCallback networkCallback) {
+            @NonNull Handler handler, @NonNull NetworkCallback networkCallback) {
         final NetworkCapabilities nc = request.networkCapabilities;
         sendRequestForNetwork(nc, networkCallback, 0, BACKGROUND_REQUEST,
-                TYPE_NONE, handler == null ? getDefaultHandler() : new CallbackHandler(handler));
+                TYPE_NONE, new CallbackHandler(handler));
     }
 
     /**
@@ -5040,4 +5121,41 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    // The first network ID of IPSec tunnel interface.
+    private static final int TUN_INTF_NETID_START = 0xFC00; // 0xFC00 = 64512
+    // The network ID range of IPSec tunnel interface.
+    private static final int TUN_INTF_NETID_RANGE = 0x0400; // 0x0400 = 1024
+
+    /**
+     * Get the network ID range reserved for IPSec tunnel interfaces.
+     *
+     * @return A Range which indicates the network ID range of IPSec tunnel interface.
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @NonNull
+    public static Range<Integer> getIpSecNetIdRange() {
+        return new Range(TUN_INTF_NETID_START, TUN_INTF_NETID_START + TUN_INTF_NETID_RANGE - 1);
+    }
+
+    /**
+     * Get private DNS mode from settings.
+     *
+     * @param cr The ContentResolver to query private DNS mode from settings.
+     * @return A string of private DNS mode as one of the PRIVATE_DNS_MODE_* constants.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @NonNull
+    @PrivateDnsMode
+    public static String getPrivateDnsMode(@NonNull ContentResolver cr) {
+        String mode = Settings.Global.getString(cr, PRIVATE_DNS_MODE);
+        if (TextUtils.isEmpty(mode)) mode = Settings.Global.getString(cr, PRIVATE_DNS_DEFAULT_MODE);
+        // If both PRIVATE_DNS_MODE and PRIVATE_DNS_DEFAULT_MODE are not set, choose
+        // PRIVATE_DNS_MODE_OPPORTUNISTIC as default mode.
+        if (TextUtils.isEmpty(mode)) mode = PRIVATE_DNS_MODE_OPPORTUNISTIC;
+        return mode;
+    }
 }
diff --git a/framework/src/android/net/IConnectivityManager.aidl b/framework/src/android/net/IConnectivityManager.aidl
index cd49258..1bbf1a9 100644
--- a/framework/src/android/net/IConnectivityManager.aidl
+++ b/framework/src/android/net/IConnectivityManager.aidl
@@ -30,6 +30,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkRequest;
+import android.net.NetworkScore;
 import android.net.NetworkState;
 import android.net.NetworkStateSnapshot;
 import android.net.OemNetworkPreferences;
@@ -138,12 +139,12 @@
     void declareNetworkRequestUnfulfillable(in NetworkRequest request);
 
     Network registerNetworkAgent(in INetworkAgent na, in NetworkInfo ni, in LinkProperties lp,
-            in NetworkCapabilities nc, int score, in NetworkAgentConfig config,
+            in NetworkCapabilities nc, in NetworkScore score, in NetworkAgentConfig config,
             in int factorySerialNumber);
 
     NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities, int reqType,
             in Messenger messenger, int timeoutSec, in IBinder binder, int legacy,
-            String callingPackageName, String callingAttributionTag);
+            int callbackFlags, String callingPackageName, String callingAttributionTag);
 
     NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities,
             in PendingIntent operation, String callingPackageName, String callingAttributionTag);
@@ -151,7 +152,7 @@
     void releasePendingNetworkRequest(in PendingIntent operation);
 
     NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities,
-            in Messenger messenger, in IBinder binder, String callingPackageName,
+            in Messenger messenger, in IBinder binder, int callbackFlags, String callingPackageName,
             String callingAttributionTag);
 
     void pendingListenForNetwork(in NetworkCapabilities networkCapabilities,
diff --git a/framework/src/android/net/IpPrefix.java b/framework/src/android/net/IpPrefix.java
index d2ee7d1..bf4481a 100644
--- a/framework/src/android/net/IpPrefix.java
+++ b/framework/src/android/net/IpPrefix.java
@@ -113,7 +113,7 @@
         // first statement in constructor". We could factor out setting the member variables to an
         // init() method, but if we did, then we'd have to make the members non-final, or "error:
         // cannot assign a value to final variable address". So we just duplicate the code here.
-        Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(prefix);
+        Pair<InetAddress, Integer> ipAndMask = NetworkUtils.legacyParseIpAndMask(prefix);
         this.address = ipAndMask.first.getAddress();
         this.prefixLength = ipAndMask.second;
         checkAndMaskAddressAndPrefixLength();
diff --git a/framework/src/android/net/LinkAddress.java b/framework/src/android/net/LinkAddress.java
index d1bdaa0..d48b8c7 100644
--- a/framework/src/android/net/LinkAddress.java
+++ b/framework/src/android/net/LinkAddress.java
@@ -325,7 +325,7 @@
     public LinkAddress(@NonNull String address, int flags, int scope) {
         // This may throw an IllegalArgumentException; catching it is the caller's responsibility.
         // TODO: consider rejecting mapped IPv4 addresses such as "::ffff:192.0.2.5/24".
-        Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(address);
+        Pair<InetAddress, Integer> ipAndMask = NetworkUtils.legacyParseIpAndMask(address);
         init(ipAndMask.first, ipAndMask.second, flags, scope, LIFETIME_UNKNOWN, LIFETIME_UNKNOWN);
     }
 
diff --git a/framework/src/android/net/Network.java b/framework/src/android/net/Network.java
index 46141e0..7245db3 100644
--- a/framework/src/android/net/Network.java
+++ b/framework/src/android/net/Network.java
@@ -30,10 +30,10 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.okhttp.internalandroidapi.Dns;
-import com.android.okhttp.internalandroidapi.HttpURLConnectionFactory;
 
 import libcore.io.IoUtils;
+import libcore.net.http.Dns;
+import libcore.net.http.HttpURLConnectionFactory;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -299,7 +299,7 @@
         // Set configuration on the HttpURLConnectionFactory that will be good for all
         // connections created by this Network. Configuration that might vary is left
         // until openConnection() and passed as arguments.
-        HttpURLConnectionFactory urlConnectionFactory = new HttpURLConnectionFactory();
+        HttpURLConnectionFactory urlConnectionFactory = HttpURLConnectionFactory.createInstance();
         urlConnectionFactory.setDns(dnsLookup); // Let traffic go via dnsLookup
         // A private connection pool just for this Network.
         urlConnectionFactory.setNewConnectionPool(httpMaxConnections,
diff --git a/framework/src/android/net/NetworkAgent.java b/framework/src/android/net/NetworkAgent.java
index 27aa15d..b3ab0ee 100644
--- a/framework/src/android/net/NetworkAgent.java
+++ b/framework/src/android/net/NetworkAgent.java
@@ -371,6 +371,14 @@
         return ni;
     }
 
+    // Temporary backward compatibility constructor
+    public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag,
+            @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
+            @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) {
+        this(context, looper, logTag, nc, lp,
+                new NetworkScore.Builder().setLegacyInt(score).build(), config, provider);
+    }
+
     /**
      * Create a new network agent.
      * @param context a {@link Context} to get system services from.
@@ -382,10 +390,12 @@
      * @param score the initial score of this network. Update with sendNetworkScore.
      * @param config an immutable {@link NetworkAgentConfig} for this agent.
      * @param provider the {@link NetworkProvider} managing this agent.
+     * @hide TODO : unhide when impl is complete
      */
     public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag,
-            @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
-            @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) {
+            @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp,
+            @NonNull NetworkScore score, @NonNull NetworkAgentConfig config,
+            @Nullable NetworkProvider provider) {
         this(looper, context, logTag, nc, lp, score, config,
                 provider == null ? NetworkProvider.ID_NONE : provider.getProviderId(),
                 getLegacyNetworkInfo(config));
@@ -395,12 +405,12 @@
         public final Context context;
         public final NetworkCapabilities capabilities;
         public final LinkProperties properties;
-        public final int score;
+        public final NetworkScore score;
         public final NetworkAgentConfig config;
         public final NetworkInfo info;
         InitialConfiguration(@NonNull Context context, @NonNull NetworkCapabilities capabilities,
-                @NonNull LinkProperties properties, int score, @NonNull NetworkAgentConfig config,
-                @NonNull NetworkInfo info) {
+                @NonNull LinkProperties properties, @NonNull NetworkScore score,
+                @NonNull NetworkAgentConfig config, @NonNull NetworkInfo info) {
             this.context = context;
             this.capabilities = capabilities;
             this.properties = properties;
@@ -412,8 +422,9 @@
     private volatile InitialConfiguration mInitialConfiguration;
 
     private NetworkAgent(@NonNull Looper looper, @NonNull Context context, @NonNull String logTag,
-            @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
-            @NonNull NetworkAgentConfig config, int providerId, @NonNull NetworkInfo ni) {
+            @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp,
+            @NonNull NetworkScore score, @NonNull NetworkAgentConfig config, int providerId,
+            @NonNull NetworkInfo ni) {
         mHandler = new NetworkAgentHandler(looper);
         LOG_TAG = logTag;
         mNetworkInfo = new NetworkInfo(ni);
@@ -875,13 +886,22 @@
     /**
      * Must be called by the agent to update the score of this network.
      *
+     * @param score the new score.
+     * @hide TODO : unhide when impl is complete
+     */
+    public final void sendNetworkScore(@NonNull NetworkScore score) {
+        Objects.requireNonNull(score);
+        queueOrSendMessage(reg -> reg.sendScore(score));
+    }
+
+    /**
+     * Must be called by the agent to update the score of this network.
+     *
      * @param score the new score, between 0 and 99.
+     * deprecated use sendNetworkScore(NetworkScore) TODO : remove in S.
      */
     public final void sendNetworkScore(@IntRange(from = 0, to = 99) int score) {
-        if (score < 0) {
-            throw new IllegalArgumentException("Score must be >= 0");
-        }
-        queueOrSendMessage(reg -> reg.sendScore(score));
+        sendNetworkScore(new NetworkScore.Builder().setLegacyInt(score).build());
     }
 
     /**
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index c82cd3b..058f3c9 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -25,6 +25,7 @@
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.net.ConnectivityManager.NetworkCallback;
+import android.net.wifi.WifiNetworkSuggestion;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -131,6 +132,7 @@
         mPrivateDnsBroken = false;
         mRequestorUid = Process.INVALID_UID;
         mRequestorPackageName = null;
+        mSubIds = new ArraySet<>();
     }
 
     /**
@@ -159,6 +161,7 @@
         mPrivateDnsBroken = nc.mPrivateDnsBroken;
         mRequestorUid = nc.mRequestorUid;
         mRequestorPackageName = nc.mRequestorPackageName;
+        mSubIds = new ArraySet<>(nc.mSubIds);
     }
 
     /**
@@ -1048,6 +1051,16 @@
      *
      * Instances of NetworkCapabilities sent to apps without the appropriate permissions will have
      * this field cleared out.
+     *
+     * <p>
+     * This field will only be populated for VPN and wifi network suggestor apps (i.e using
+     * {@link WifiNetworkSuggestion}), and only for the network they own.
+     * In the case of wifi network suggestors apps, this field is also location sensitive, so the
+     * app needs to hold {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission. If the
+     * app targets SDK version greater than or equal to {@link Build.VERSION_CODES#S}, then they
+     * also need to use {@link NetworkCallback#FLAG_INCLUDE_LOCATION_INFO} to get the info in their
+     * callback. The app will be blamed for location access if this field is included.
+     * </p>
      */
     public int getOwnerUid() {
         return mOwnerUid;
@@ -1655,6 +1668,7 @@
         combineSSIDs(nc);
         combineRequestor(nc);
         combineAdministratorUids(nc);
+        combineSubIds(nc);
     }
 
     /**
@@ -1674,8 +1688,9 @@
                 && satisfiedBySpecifier(nc)
                 && (onlyImmutable || satisfiedBySignalStrength(nc))
                 && (onlyImmutable || satisfiedByUids(nc))
-                && (onlyImmutable || satisfiedBySSID(nc)))
-                && (onlyImmutable || satisfiedByRequestor(nc));
+                && (onlyImmutable || satisfiedBySSID(nc))
+                && (onlyImmutable || satisfiedByRequestor(nc))
+                && (onlyImmutable || satisfiedBySubIds(nc)));
     }
 
     /**
@@ -1771,7 +1786,8 @@
                 && equalsOwnerUid(that)
                 && equalsPrivateDnsBroken(that)
                 && equalsRequestor(that)
-                && equalsAdministratorUids(that);
+                && equalsAdministratorUids(that)
+                && equalsSubIds(that);
     }
 
     @Override
@@ -1793,7 +1809,8 @@
                 + Objects.hashCode(mPrivateDnsBroken) * 47
                 + Objects.hashCode(mRequestorUid) * 53
                 + Objects.hashCode(mRequestorPackageName) * 59
-                + Arrays.hashCode(mAdministratorUids) * 61;
+                + Arrays.hashCode(mAdministratorUids) * 61
+                + Objects.hashCode(mSubIds) * 67;
     }
 
     @Override
@@ -1827,6 +1844,7 @@
         dest.writeInt(mOwnerUid);
         dest.writeInt(mRequestorUid);
         dest.writeString(mRequestorPackageName);
+        dest.writeIntArray(CollectionUtils.toIntArray(mSubIds));
     }
 
     public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -1850,6 +1868,11 @@
                 netCap.mOwnerUid = in.readInt();
                 netCap.mRequestorUid = in.readInt();
                 netCap.mRequestorPackageName = in.readString();
+                netCap.mSubIds = new ArraySet<>();
+                final int[] subIdInts = Objects.requireNonNull(in.createIntArray());
+                for (int i = 0; i < subIdInts.length; i++) {
+                    netCap.mSubIds.add(subIdInts[i]);
+                }
                 return netCap;
             }
             @Override
@@ -1933,11 +1956,14 @@
             sb.append(" SSID: ").append(mSSID);
         }
 
-
         if (mPrivateDnsBroken) {
             sb.append(" PrivateDnsBroken");
         }
 
+        if (!mSubIds.isEmpty()) {
+            sb.append(" SubscriptionIds: ").append(mSubIds);
+        }
+
         sb.append("]");
         return sb.toString();
     }
@@ -2251,6 +2277,67 @@
     }
 
     /**
+     * Set of the subscription IDs that identifies the network or request, empty if none.
+     */
+    @NonNull
+    private ArraySet<Integer> mSubIds = new ArraySet<>();
+
+    /**
+     * Sets the subscription ID set that associated to this network or request.
+     *
+     * @hide
+     */
+    @NonNull
+    public NetworkCapabilities setSubIds(@NonNull Set<Integer> subIds) {
+        mSubIds = new ArraySet(Objects.requireNonNull(subIds));
+        return this;
+    }
+
+    /**
+     * Gets the subscription ID set that associated to this network or request.
+     * @return
+     */
+    @NonNull
+    public Set<Integer> getSubIds() {
+        return new ArraySet<>(mSubIds);
+    }
+
+    /**
+     * Tests if the subscription ID set of this network is the same as that of the passed one.
+     */
+    private boolean equalsSubIds(@NonNull NetworkCapabilities nc) {
+        return Objects.equals(mSubIds, nc.mSubIds);
+    }
+
+    /**
+     * Check if the subscription ID set requirements of this object are matched by the passed one.
+     * If specified in the request, the passed one need to have at least one subId and at least
+     * one of them needs to be in the request set.
+     */
+    private boolean satisfiedBySubIds(@NonNull NetworkCapabilities nc) {
+        if (mSubIds.isEmpty()) return true;
+        if (nc.mSubIds.isEmpty()) return false;
+        for (final Integer subId : nc.mSubIds) {
+            if (mSubIds.contains(subId)) return true;
+        }
+        return false;
+    }
+
+    /**
+     * Combine subscription ID set of the capabilities.
+     *
+     * <p>This is only legal if the subscription Ids are equal.
+     *
+     * <p>If both subscription IDs are not equal, they belong to different subscription
+     * (or no subscription). In this case, it would not make sense to add them together.
+     */
+    private void combineSubIds(@NonNull NetworkCapabilities nc) {
+        if (!Objects.equals(mSubIds, nc.mSubIds)) {
+            throw new IllegalStateException("Can't combine two subscription ID sets");
+        }
+    }
+
+    /**
      * Builder class for NetworkCapabilities.
      *
      * This class is mainly for for {@link NetworkAgent} instances to use. Many fields in
@@ -2556,6 +2643,18 @@
         }
 
         /**
+         * Set the subscription ID set.
+         *
+         * @param subIds a set that represent the subscription IDs. Empty if clean up.
+         * @return this builder.
+         */
+        @NonNull
+        public Builder setSubIds(@NonNull final Set<Integer> subIds) {
+            mCaps.setSubIds(subIds);
+            return this;
+        }
+
+        /**
          * Builds the instance of the capabilities.
          *
          * @return the built instance of NetworkCapabilities.
diff --git a/framework/src/android/net/NetworkInfo.java b/framework/src/android/net/NetworkInfo.java
index d752901..bb23494 100644
--- a/framework/src/android/net/NetworkInfo.java
+++ b/framework/src/android/net/NetworkInfo.java
@@ -21,7 +21,6 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.Annotation.NetworkType;
 import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -164,7 +163,7 @@
      * @param typeName a human-readable string for the network type, or an empty string or null.
      * @param subtypeName a human-readable string for the subtype, or an empty string or null.
      */
-    public NetworkInfo(int type, @NetworkType int subtype,
+    public NetworkInfo(int type, int subtype,
             @Nullable String typeName, @Nullable String subtypeName) {
         if (!ConnectivityManager.isNetworkTypeValid(type)
                 && type != ConnectivityManager.TYPE_NONE) {
diff --git a/framework/src/android/net/NetworkRequest.java b/framework/src/android/net/NetworkRequest.java
index b4a651c..dbe3ecc 100644
--- a/framework/src/android/net/NetworkRequest.java
+++ b/framework/src/android/net/NetworkRequest.java
@@ -31,6 +31,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -113,6 +114,10 @@
      *       for the network (if any) that satisfies the default Internet
      *       request.
      *
+     *     - TRACK_BEST, which causes the framework to send callbacks about
+     *       the single, highest scoring current network (if any) that matches
+     *       the specified NetworkCapabilities.
+     *
      *     - BACKGROUND_REQUEST, like REQUEST but does not cause any networks
      *       to retain the NET_CAPABILITY_FOREGROUND capability. A network with
      *       no foreground requests is in the background. A network that has
@@ -135,6 +140,7 @@
         REQUEST,
         BACKGROUND_REQUEST,
         TRACK_SYSTEM_DEFAULT,
+        LISTEN_FOR_BEST,
     };
 
     /**
@@ -382,11 +388,17 @@
                 return setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
                         .setSubscriptionId(subId).build());
             } catch (NumberFormatException nfe) {
-                // A StringNetworkSpecifier does not accept null or empty ("") strings. When network
-                // specifiers were strings a null string and an empty string were considered
-                // equivalent. Hence no meaning is attached to a null or empty ("") string.
-                return setNetworkSpecifier(TextUtils.isEmpty(networkSpecifier) ? null
-                        : new StringNetworkSpecifier(networkSpecifier));
+                // An EthernetNetworkSpecifier or TestNetworkSpecifier does not accept null or empty
+                // ("") strings. When network specifiers were strings a null string and an empty
+                // string were considered equivalent. Hence no meaning is attached to a null or
+                // empty ("") string.
+                if (TextUtils.isEmpty(networkSpecifier)) {
+                    return setNetworkSpecifier((NetworkSpecifier) null);
+                } else if (mNetworkCapabilities.hasTransport(TRANSPORT_TEST)) {
+                    return setNetworkSpecifier(new TestNetworkSpecifier(networkSpecifier));
+                } else {
+                    return setNetworkSpecifier(new EthernetNetworkSpecifier(networkSpecifier));
+                }
             }
         }
 
@@ -449,6 +461,21 @@
             }
             nc.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         }
+
+        /**
+         * Sets the optional subscription ID set.
+         * <p>
+         * This specify the subscription IDs requirement.
+         * A network will satisfy this request only if it matches one of the subIds in this set.
+         * An empty set matches all networks, including those without a subId.
+         *
+         * @param subIds A {@code Set} that represents subscription IDs.
+         */
+        @NonNull
+        public Builder setSubIds(@NonNull Set<Integer> subIds) {
+            mNetworkCapabilities.setSubIds(subIds);
+            return this;
+        }
     }
 
     // implement the Parcelable interface
@@ -487,6 +514,15 @@
     }
 
     /**
+     * Returns true iff. this NetworkRequest is of type LISTEN_FOR_BEST.
+     *
+     * @hide
+     */
+    public boolean isListenForBest() {
+        return type == Type.LISTEN_FOR_BEST;
+    }
+
+    /**
      * Returns true iff. the contained NetworkRequest is one that:
      *
      *     - should be associated with at most one satisfying network
diff --git a/framework/src/android/net/NetworkState.java b/framework/src/android/net/NetworkState.java
index d010265..9b69674 100644
--- a/framework/src/android/net/NetworkState.java
+++ b/framework/src/android/net/NetworkState.java
@@ -22,7 +22,7 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.Slog;
+import android.util.Log;
 
 /**
  * Snapshot of network state.
@@ -83,7 +83,7 @@
         if (VALIDATE_ROAMING_STATE && networkInfo != null && networkCapabilities != null) {
             if (networkInfo.isRoaming() == networkCapabilities
                     .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)) {
-                Slog.wtf("NetworkState", "Roaming state disagreement between " + networkInfo
+                Log.wtf("NetworkState", "Roaming state disagreement between " + networkInfo
                         + " and " + networkCapabilities);
             }
         }
diff --git a/framework/src/android/net/NetworkUtils.java b/framework/src/android/net/NetworkUtils.java
index 9e42bbe..c0f2628 100644
--- a/framework/src/android/net/NetworkUtils.java
+++ b/framework/src/android/net/NetworkUtils.java
@@ -27,8 +27,10 @@
 import java.io.FileDescriptor;
 import java.math.BigInteger;
 import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.SocketException;
+import java.net.UnknownHostException;
 import java.util.Locale;
 import java.util.TreeSet;
 
@@ -212,7 +214,7 @@
     @Deprecated
     public static InetAddress numericToInetAddress(String addrString)
             throws IllegalArgumentException {
-        return InetAddress.parseNumericAddress(addrString);
+        return InetAddresses.parseNumericAddress(addrString);
     }
 
     /**
@@ -234,7 +236,7 @@
         try {
             String[] pieces = ipAndMaskString.split("/", 2);
             prefixLength = Integer.parseInt(pieces[1]);
-            address = InetAddress.parseNumericAddress(pieces[0]);
+            address = InetAddresses.parseNumericAddress(pieces[0]);
         } catch (NullPointerException e) {            // Null string.
         } catch (ArrayIndexOutOfBoundsException e) {  // No prefix length.
         } catch (NumberFormatException e) {           // Non-numeric prefix.
@@ -249,6 +251,47 @@
     }
 
     /**
+     * Utility method to parse strings such as "192.0.2.5/24" or "2001:db8::cafe:d00d/64".
+     * @hide
+     *
+     * @deprecated This method is used only for IpPrefix and LinkAddress. Since Android S, use
+     *             {@link #parseIpAndMask(String)}, if possible.
+     */
+    @Deprecated
+    public static Pair<InetAddress, Integer> legacyParseIpAndMask(String ipAndMaskString) {
+        InetAddress address = null;
+        int prefixLength = -1;
+        try {
+            String[] pieces = ipAndMaskString.split("/", 2);
+            prefixLength = Integer.parseInt(pieces[1]);
+            if (pieces[0] == null || pieces[0].isEmpty()) {
+                final byte[] bytes = new byte[16];
+                bytes[15] = 1;
+                return new Pair<InetAddress, Integer>(Inet6Address.getByAddress(
+                        "ip6-localhost"/* host */, bytes, 0 /* scope_id */), prefixLength);
+            }
+
+            if (pieces[0].startsWith("[")
+                    && pieces[0].endsWith("]")
+                    && pieces[0].indexOf(':') != -1) {
+                pieces[0] = pieces[0].substring(1, pieces[0].length() - 1);
+            }
+            address = InetAddresses.parseNumericAddress(pieces[0]);
+        } catch (NullPointerException e) {            // Null string.
+        } catch (ArrayIndexOutOfBoundsException e) {  // No prefix length.
+        } catch (NumberFormatException e) {           // Non-numeric prefix.
+        } catch (IllegalArgumentException e) {        // Invalid IP address.
+        } catch (UnknownHostException e) {            // IP address length is illegal
+        }
+
+        if (address == null || prefixLength == -1) {
+            throw new IllegalArgumentException("Invalid IP address and mask " + ipAndMaskString);
+        }
+
+        return new Pair<InetAddress, Integer>(address, prefixLength);
+    }
+
+    /**
      * Convert a 32 char hex string into a Inet6Address.
      * throws a runtime exception if the string isn't 32 chars, isn't hex or can't be
      * made into an Inet6Address
diff --git a/framework/src/android/net/ParseException.java b/framework/src/android/net/ParseException.java
new file mode 100644
index 0000000..ca6d012
--- /dev/null
+++ b/framework/src/android/net/ParseException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2006 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.SystemApi;
+
+/**
+ * Thrown when parsing failed.
+ */
+// See non-public class {@link WebAddress}.
+public class ParseException extends RuntimeException {
+    public String response;
+
+    /** @hide */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public ParseException(@NonNull String response) {
+        super(response);
+        this.response = response;
+    }
+
+    /** @hide */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public ParseException(@NonNull String response, @NonNull Throwable cause) {
+        super(response, cause);
+        this.response = response;
+    }
+}
diff --git a/framework/src/android/net/ProxyInfo.java b/framework/src/android/net/ProxyInfo.java
index 229db0d..745e20f 100644
--- a/framework/src/android/net/ProxyInfo.java
+++ b/framework/src/android/net/ProxyInfo.java
@@ -129,7 +129,7 @@
     }
 
     /**
-     * Only used in PacProxyInstaller after Local Proxy is bound.
+     * Only used in PacProxyService after Local Proxy is bound.
      * @hide
      */
     public ProxyInfo(@NonNull Uri pacFileUrl, int localProxyPort) {
diff --git a/framework/src/android/net/QosSocketInfo.java b/framework/src/android/net/QosSocketInfo.java
index d37c469..53d9669 100644
--- a/framework/src/android/net/QosSocketInfo.java
+++ b/framework/src/android/net/QosSocketInfo.java
@@ -92,7 +92,7 @@
         Objects.requireNonNull(socket, "socket cannot be null");
 
         mNetwork = Objects.requireNonNull(network, "network cannot be null");
-        mParcelFileDescriptor = ParcelFileDescriptor.dup(socket.getFileDescriptor$());
+        mParcelFileDescriptor = ParcelFileDescriptor.fromSocket(socket);
         mLocalSocketAddress =
                 new InetSocketAddress(socket.getLocalAddress(), socket.getLocalPort());
     }
@@ -114,10 +114,10 @@
         try {
             return new InetSocketAddress(InetAddress.getByAddress(address), port);
         } catch (final UnknownHostException e) {
-            /* The catch block was purposely left empty.  UnknownHostException will never be thrown
+            /* This can never happen. UnknownHostException will never be thrown
                since the address provided is numeric and non-null. */
+            throw new RuntimeException("UnknownHostException on numeric address", e);
         }
-        return new InetSocketAddress();
     }
 
     @Override
diff --git a/framework/src/android/net/TestNetworkSpecifier.java b/framework/src/android/net/TestNetworkSpecifier.java
new file mode 100644
index 0000000..b7470a5
--- /dev/null
+++ b/framework/src/android/net/TestNetworkSpecifier.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * A {@link NetworkSpecifier} used to identify test interfaces.
+ *
+ * @see TestNetworkManager
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class TestNetworkSpecifier extends NetworkSpecifier implements Parcelable {
+
+    /**
+     * Name of the network interface.
+     */
+    @NonNull
+    private final String mInterfaceName;
+
+    public TestNetworkSpecifier(@NonNull String interfaceName) {
+        Preconditions.checkStringNotEmpty(interfaceName);
+        mInterfaceName = interfaceName;
+    }
+
+    // This may be null in the future to support specifiers based on data other than the interface
+    // name.
+    @Nullable
+    public String getInterfaceName() {
+        return mInterfaceName;
+    }
+
+    @Override
+    public boolean canBeSatisfiedBy(@Nullable NetworkSpecifier other) {
+        return equals(other);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof TestNetworkSpecifier)) return false;
+        return TextUtils.equals(mInterfaceName, ((TestNetworkSpecifier) o).mInterfaceName);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(mInterfaceName);
+    }
+
+    @Override
+    public String toString() {
+        return "TestNetworkSpecifier (" + mInterfaceName + ")";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mInterfaceName);
+    }
+
+    public static final @NonNull Creator<TestNetworkSpecifier> CREATOR =
+            new Creator<TestNetworkSpecifier>() {
+        public TestNetworkSpecifier createFromParcel(Parcel in) {
+            return new TestNetworkSpecifier(in.readString());
+        }
+        public TestNetworkSpecifier[] newArray(int size) {
+            return new TestNetworkSpecifier[size];
+        }
+    };
+}
diff --git a/framework/src/android/net/util/MultinetworkPolicyTracker.java b/framework/src/android/net/util/MultinetworkPolicyTracker.java
index 43fffd7..739ddad 100644
--- a/framework/src/android/net/util/MultinetworkPolicyTracker.java
+++ b/framework/src/android/net/util/MultinetworkPolicyTracker.java
@@ -30,8 +30,8 @@
 import android.net.Uri;
 import android.os.Handler;
 import android.provider.Settings;
-import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
@@ -92,8 +92,8 @@
     }
 
     @VisibleForTesting
-    protected class ActiveDataSubscriptionIdChangedListener extends PhoneStateListener
-            implements PhoneStateListener.ActiveDataSubscriptionIdChangedListener {
+    protected class ActiveDataSubscriptionIdListener extends TelephonyCallback
+            implements TelephonyCallback.ActiveDataSubscriptionIdListener {
         @Override
         public void onActiveDataSubscriptionIdChanged(int subId) {
             mActiveSubId = subId;
@@ -121,8 +121,8 @@
             }
         };
 
-        ctx.getSystemService(TelephonyManager.class).registerPhoneStateListener(
-                new HandlerExecutor(handler), new ActiveDataSubscriptionIdChangedListener());
+        ctx.getSystemService(TelephonyManager.class).registerTelephonyCallback(
+                new HandlerExecutor(handler), new ActiveDataSubscriptionIdListener());
 
         updateAvoidBadWifi();
         updateMeteredMultipathPreference();
diff --git a/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl b/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl
index f0193db..18d26a7 100644
--- a/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl
+++ b/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl
@@ -19,11 +19,12 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
+import android.net.NetworkScore;
 import android.net.QosSession;
 import android.telephony.data.EpsBearerQosSessionAttributes;
 
 /**
- * Interface for NetworkAgents to send network network properties.
+ * Interface for NetworkAgents to send network properties.
  * @hide
  */
 oneway interface INetworkAgentRegistry {
@@ -31,7 +32,7 @@
     void sendLinkProperties(in LinkProperties lp);
     // TODO: consider replacing this by "markConnected()" and removing
     void sendNetworkInfo(in NetworkInfo info);
-    void sendScore(int score);
+    void sendScore(in NetworkScore score);
     void sendExplicitlySelected(boolean explicitlySelected, boolean acceptPartial);
     void sendSocketKeepaliveEvent(int slot, int reason);
     void sendUnderlyingNetworks(in @nullable List<Network> networks);
diff --git a/service/Android.bp b/service/Android.bp
index e65b7b4..f630cea 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -50,26 +50,40 @@
 }
 
 java_library {
-    name: "service-connectivity",
+    name: "service-connectivity-pre-jarjar",
     srcs: [
+        ":framework-connectivity-shared-srcs",
         ":connectivity-service-srcs",
     ],
-    installable: true,
-    jarjar_rules: "jarjar-rules.txt",
     libs: [
         "android.net.ipsec.ike",
         "services.core",
         "services.net",
         "unsupportedappusage",
+        "ServiceConnectivityResources",
     ],
     static_libs: [
         "modules-utils-os",
         "net-utils-device-common",
         "net-utils-framework-common",
         "netd-client",
+        "PlatformProperties",
     ],
     apex_available: [
         "//apex_available:platform",
         "com.android.tethering",
     ],
 }
+
+java_library {
+    name: "service-connectivity",
+    installable: true,
+    static_libs: [
+        "service-connectivity-pre-jarjar",
+    ],
+    jarjar_rules: "jarjar-rules.txt",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.tethering",
+    ],
+}
diff --git a/service/ServiceConnectivityResources/Android.bp b/service/ServiceConnectivityResources/Android.bp
new file mode 100644
index 0000000..f2446b7
--- /dev/null
+++ b/service/ServiceConnectivityResources/Android.bp
@@ -0,0 +1,35 @@
+//
+// 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.
+//
+
+// APK to hold all the wifi overlayable resources.
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_app {
+    name: "ServiceConnectivityResources",
+    sdk_version: "system_current",
+    resource_dirs: [
+        "res",
+    ],
+    privileged: true,
+    export_package_resources: true,
+    apex_available: [
+        "com.android.tethering",
+    ],
+    // TODO: use a dedicated cert once generated
+    certificate: "platform",
+}
diff --git a/service/ServiceConnectivityResources/AndroidManifest.xml b/service/ServiceConnectivityResources/AndroidManifest.xml
new file mode 100644
index 0000000..2c30302
--- /dev/null
+++ b/service/ServiceConnectivityResources/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<!-- Manifest for connectivity resources APK -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.connectivity.resources"
+          coreApp="true"
+          android:versionCode="1"
+          android:versionName="S-initial">
+    <application
+        android:label="@string/connectivityResourcesAppLabel"
+        android:defaultToDeviceProtectedStorage="true"
+        android:directBootAware="true">
+        <!-- This is only used to identify this app by resolving the action.
+             The activity is never actually triggered. -->
+        <activity android:name="android.app.Activity" android:exported="true" android:enabled="true">
+            <intent-filter>
+                <action android:name="com.android.server.connectivity.intent.action.SERVICE_CONNECTIVITY_RESOURCES_APK" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/service/ServiceConnectivityResources/res/values/config.xml b/service/ServiceConnectivityResources/res/values/config.xml
new file mode 100644
index 0000000..7d98c76
--- /dev/null
+++ b/service/ServiceConnectivityResources/res/values/config.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<!-- Configuration values for ConnectivityService
+     DO NOT EDIT THIS FILE for specific device configuration; instead, use a Runtime Resources
+     Overlay package following the overlayable.xml configuration in the same directory:
+     https://source.android.com/devices/architecture/rros -->
+<resources>
+
+    <!-- Configuration hook for the URL returned by ConnectivityManager#getCaptivePortalServerUrl.
+         If empty, the returned value is controlled by Settings.Global.CAPTIVE_PORTAL_HTTP_URL,
+         and if that value is empty, the framework will use a hard-coded default.
+         This is *NOT* a URL that will always be used by the system network validation to detect
+         captive portals: NetworkMonitor may use different strategies and will not necessarily use
+         this URL. NetworkMonitor behaviour should be configured with NetworkStack resource overlays
+         instead. -->
+    <!--suppress CheckTagEmptyBody -->
+    <string translatable="false" name="config_networkCaptivePortalServerUrl"></string>
+
+    <!-- The maximum duration (in milliseconds) we expect a network transition to take -->
+    <integer name="config_networkTransitionTimeout">60000</integer>
+
+    <!-- Configuration of network interfaces that support WakeOnLAN -->
+    <string-array translatable="false" name="config_wakeonlan_supported_interfaces">
+        <!--
+        <item>wlan0</item>
+        <item>eth0</item>
+        -->
+    </string-array>
+
+</resources>
\ No newline at end of file
diff --git a/service/ServiceConnectivityResources/res/values/overlayable.xml b/service/ServiceConnectivityResources/res/values/overlayable.xml
new file mode 100644
index 0000000..00ec2df
--- /dev/null
+++ b/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!-- 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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <overlayable name="ServiceConnectivityResourcesConfig">
+        <policy type="product|system|vendor">
+            <!-- Configuration values for ConnectivityService -->
+            <item type="string" name="config_networkCaptivePortalServerUrl"/>
+            <item type="integer" name="config_networkTransitionTimeout"/>
+            <item type="array" name="config_wakeonlan_supported_interfaces"/>
+
+
+        </policy>
+    </overlayable>
+</resources>
diff --git a/service/ServiceConnectivityResources/res/values/strings.xml b/service/ServiceConnectivityResources/res/values/strings.xml
new file mode 100644
index 0000000..2c7b992
--- /dev/null
+++ b/service/ServiceConnectivityResources/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources>
+    <!-- The System Connectivity Resources package is an internal system package that provides
+         configuration values for system networking that were pre-configured in the device. This
+         is the name of the package to display in the list of system apps. [CHAR LIMIT=40] -->
+    <string name="connectivityResourcesAppLabel">System Connectivity Resources</string>
+</resources>
\ No newline at end of file
diff --git a/service/jarjar-rules.txt b/service/jarjar-rules.txt
index d8205bf..a7b419b 100644
--- a/service/jarjar-rules.txt
+++ b/service/jarjar-rules.txt
@@ -1,2 +1,14 @@
+rule android.sysprop.** com.android.connectivity.sysprop.@1
 rule com.android.net.module.util.** com.android.connectivity.net-utils.@1
-rule com.android.modules.utils.** com.android.connectivity.modules-utils.@1
\ No newline at end of file
+rule com.android.modules.utils.** com.android.connectivity.modules-utils.@1
+
+# internal util classes
+# Exclude AsyncChannel. TODO: remove AsyncChannel usage in ConnectivityService
+rule com.android.internal.util.AsyncChannel* @0
+# Exclude LocationPermissionChecker. This is going to be moved to libs/net
+rule com.android.internal.util.LocationPermissionChecker* @0
+rule android.util.LocalLog* com.android.connectivity.util.LocalLog@1
+# android.util.IndentingPrintWriter* should use a different package name from
+# the one in com.android.internal.util
+rule android.util.IndentingPrintWriter* android.connectivity.util.IndentingPrintWriter@1
+rule com.android.internal.util.** com.android.connectivity.util.@1
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 2c9837d..464f588 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -55,6 +55,7 @@
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkPolicyManager.RULE_NONE;
 import static android.net.NetworkPolicyManager.uidRulesToString;
+import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST;
 import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired;
 import static android.os.Process.INVALID_UID;
 import static android.os.Process.VPN_UID;
@@ -85,6 +86,7 @@
 import android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
 import android.net.ConnectivityDiagnosticsManager.DataStallReport;
 import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
 import android.net.DataStallReportParcelable;
 import android.net.DnsResolverServiceManager;
 import android.net.ICaptivePortal;
@@ -116,6 +118,7 @@
 import android.net.NetworkPolicyManager;
 import android.net.NetworkProvider;
 import android.net.NetworkRequest;
+import android.net.NetworkScore;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
 import android.net.NetworkStackClient;
@@ -175,6 +178,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.sysprop.NetworkProperties;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -186,23 +190,20 @@
 import android.util.SparseIntArray;
 
 import com.android.connectivity.aidl.INetworkAgent;
-import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.LocationPermissionChecker;
 import com.android.internal.util.MessageUtils;
 import com.android.modules.utils.BasicShellCommandHandler;
 import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
 import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
+import com.android.net.module.util.LocationPermissionChecker;
 import com.android.net.module.util.NetworkCapabilitiesUtils;
 import com.android.net.module.util.PermissionUtils;
-import com.android.server.am.BatteryStatsService;
 import com.android.server.connectivity.AutodestructReference;
+import com.android.server.connectivity.ConnectivityResources;
 import com.android.server.connectivity.DnsManager;
 import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
 import com.android.server.connectivity.KeepaliveTracker;
@@ -217,7 +218,6 @@
 import com.android.server.connectivity.ProxyTracker;
 import com.android.server.connectivity.QosCallbackTracker;
 import com.android.server.net.NetworkPolicyManagerInternal;
-import com.android.server.utils.PriorityDump;
 
 import libcore.io.IoUtils;
 
@@ -319,6 +319,7 @@
     private boolean mRestrictBackground;
 
     private final Context mContext;
+    private final ConnectivityResources mResources;
     // The Context is created for UserHandle.ALL.
     private final Context mUserAllContext;
     private final Dependencies mDeps;
@@ -346,8 +347,7 @@
     private String mCurrentTcpBufferSizes;
 
     private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(
-            new Class[] { AsyncChannel.class, ConnectivityService.class, NetworkAgent.class,
-                    NetworkAgentInfo.class });
+            new Class[] { ConnectivityService.class, NetworkAgent.class, NetworkAgentInfo.class });
 
     private enum ReapUnvalidatedNetworks {
         // Tear down networks that have no chance (e.g. even if validated) of becoming
@@ -607,7 +607,6 @@
     private Intent mInitialBroadcast;
 
     private PowerManager.WakeLock mNetTransitionWakeLock;
-    private int mNetTransitionWakeLockTimeout;
     private final PowerManager.WakeLock mPendingIntentWakeLock;
 
     // A helper object to track the current default HTTP proxy. ConnectivityService needs to tell
@@ -884,27 +883,59 @@
     }
     private final LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker(this);
 
+    final LocalPriorityDump mPriorityDumper = new LocalPriorityDump();
     /**
      * Helper class which parses out priority arguments and dumps sections according to their
      * priority. If priority arguments are omitted, function calls the legacy dump command.
      */
-    private final PriorityDump.PriorityDumper mPriorityDumper = new PriorityDump.PriorityDumper() {
-        @Override
-        public void dumpHigh(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
-            doDump(fd, pw, new String[] {DIAG_ARG}, asProto);
-            doDump(fd, pw, new String[] {SHORT_ARG}, asProto);
+    private class LocalPriorityDump {
+        private static final String PRIORITY_ARG = "--dump-priority";
+        private static final String PRIORITY_ARG_HIGH = "HIGH";
+        private static final String PRIORITY_ARG_NORMAL = "NORMAL";
+
+        LocalPriorityDump() {}
+
+        private void dumpHigh(FileDescriptor fd, PrintWriter pw) {
+            doDump(fd, pw, new String[] {DIAG_ARG});
+            doDump(fd, pw, new String[] {SHORT_ARG});
         }
 
-        @Override
-        public void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
-            doDump(fd, pw, args, asProto);
+        private void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args) {
+            doDump(fd, pw, args);
         }
 
-        @Override
-        public void dump(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
-           doDump(fd, pw, args, asProto);
+        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (args == null) {
+                dumpNormal(fd, pw, args);
+                return;
+            }
+
+            String priority = null;
+            for (int argIndex = 0; argIndex < args.length; argIndex++) {
+                if (args[argIndex].equals(PRIORITY_ARG) && argIndex + 1 < args.length) {
+                    argIndex++;
+                    priority = args[argIndex];
+                }
+            }
+
+            if (PRIORITY_ARG_HIGH.equals(priority)) {
+                dumpHigh(fd, pw);
+            } else if (PRIORITY_ARG_NORMAL.equals(priority)) {
+                dumpNormal(fd, pw, args);
+            } else {
+                // ConnectivityService publishes binder service using publishBinderService() with
+                // no priority assigned will be treated as NORMAL priority. Dumpsys does not send
+                // "--dump-priority" arguments to the service. Thus, dump both NORMAL and HIGH to
+                // align the legacy design.
+                // TODO: Integrate into signal dump.
+                dumpNormal(fd, pw, args);
+                pw.println();
+                pw.println("DUMP OF SERVICE HIGH connectivity");
+                pw.println();
+                dumpHigh(fd, pw);
+            }
         }
-    };
+    }
 
     /**
      * Keeps track of the number of requests made under different uids.
@@ -983,6 +1014,13 @@
         }
 
         /**
+         * Get the {@link ConnectivityResources} to use in ConnectivityService.
+         */
+        public ConnectivityResources getResources(@NonNull Context ctx) {
+            return new ConnectivityResources(ctx);
+        }
+
+        /**
          * Create a HandlerThread to use in ConnectivityService.
          */
         public HandlerThread makeHandlerThread() {
@@ -1035,8 +1073,18 @@
             return new MultinetworkPolicyTracker(c, h, r);
         }
 
-        public IBatteryStats getBatteryStatsService() {
-            return BatteryStatsService.getService();
+        /**
+         * @see BatteryStatsManager
+         */
+        public void reportNetworkInterfaceForTransports(Context context, String iface,
+                int[] transportTypes) {
+            final BatteryStatsManager batteryStats =
+                    context.getSystemService(BatteryStatsManager.class);
+            batteryStats.reportNetworkInterfaceForTransports(iface, transportTypes);
+        }
+
+        public boolean getCellular464XlatEnabled() {
+            return NetworkProperties.isCellular464XlatEnabled().orElse(true);
         }
     }
 
@@ -1054,13 +1102,15 @@
         mSystemProperties = mDeps.getSystemProperties();
         mNetIdManager = mDeps.makeNetIdManager();
         mContext = Objects.requireNonNull(context, "missing Context");
+        mResources = deps.getResources(mContext);
         mNetworkRequestCounter = new PerUidCounter(MAX_NETWORK_REQUESTS_PER_UID);
 
         mMetricsLog = logger;
         mNetworkRanker = new NetworkRanker();
         final NetworkRequest defaultInternetRequest = createDefaultRequest();
         mDefaultRequest = new NetworkRequestInfo(
-                defaultInternetRequest, null, new Binder(),
+                defaultInternetRequest, null,
+                new Binder(), NetworkCallback.FLAG_INCLUDE_LOCATION_INFO,
                 null /* attributionTags */);
         mNetworkRequests.put(defaultInternetRequest, mDefaultRequest);
         mDefaultNetworkRequests.add(mDefaultRequest);
@@ -1114,8 +1164,6 @@
         final PowerManager powerManager = (PowerManager) context.getSystemService(
                 Context.POWER_SERVICE);
         mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-        mNetTransitionWakeLockTimeout = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_networkTransitionTimeout);
         mPendingIntentWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
 
         mNetConfigs = new NetworkConfig[ConnectivityManager.MAX_NETWORK_TYPE+1];
@@ -1182,10 +1230,6 @@
             }
         }
 
-        mWolSupportedInterfaces = new ArraySet(
-                mContext.getResources().getStringArray(
-                        com.android.internal.R.array.config_wakeonlan_supported_interfaces));
-
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
 
         mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
@@ -1231,13 +1275,18 @@
         mDnsManager = new DnsManager(mContext, mDnsResolver);
         registerPrivateDnsSettingsCallbacks();
 
+        // This NAI is a sentinel used to offer no service to apps that are on a multi-layer
+        // request that doesn't allow fallback to the default network. It should never be visible
+        // to apps. As such, it's not in the list of NAIs and doesn't need many of the normal
+        // arguments like the handler or the DnsResolver.
+        // TODO : remove this ; it is probably better handled with a sentinel request.
         mNoServiceNetwork = new NetworkAgentInfo(null,
                 new Network(NO_SERVICE_NET_ID),
                 new NetworkInfo(TYPE_NONE, 0, "", ""),
-                new LinkProperties(), new NetworkCapabilities(), 0, mContext,
-                null, new NetworkAgentConfig(), this, null,
-                null, 0, INVALID_UID,
-                mQosCallbackTracker);
+                new LinkProperties(), new NetworkCapabilities(),
+                new NetworkScore.Builder().setLegacyInt(0).build(), mContext, null,
+                new NetworkAgentConfig(), this, null, null, 0, INVALID_UID, mQosCallbackTracker,
+                mDeps);
     }
 
     private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
@@ -1317,7 +1366,9 @@
 
         if (enable) {
             handleRegisterNetworkRequest(new NetworkRequestInfo(
-                    networkRequest, null, new Binder(),
+                    networkRequest, null,
+                    new Binder(),
+                    NetworkCallback.FLAG_INCLUDE_LOCATION_INFO,
                     null /* attributionTags */));
         } else {
             handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID,
@@ -1479,11 +1530,11 @@
         // but only exists if an app asks about them or requests them. Ensure the requesting app
         // gets the type it asks for.
         filtered.setType(type);
-        final DetailedState state = isNetworkWithCapabilitiesBlocked(nc, uid, ignoreBlocked)
-                ? DetailedState.BLOCKED
-                : filtered.getDetailedState();
-        filtered.setDetailedState(getLegacyLockdownState(state),
-                "" /* reason */, null /* extraInfo */);
+        if (isNetworkWithCapabilitiesBlocked(nc, uid, ignoreBlocked)) {
+            filtered.setDetailedState(DetailedState.BLOCKED, null /* reason */,
+                    null /* extraInfo */);
+        }
+        filterForLegacyLockdown(filtered);
         return filtered;
     }
 
@@ -1559,8 +1610,8 @@
         final DetailedState state = isNetworkWithCapabilitiesBlocked(nc, uid, false)
                 ? DetailedState.BLOCKED
                 : DetailedState.DISCONNECTED;
-        info.setDetailedState(getLegacyLockdownState(state),
-                "" /* reason */, null /* extraInfo */);
+        info.setDetailedState(state, null /* reason */, null /* extraInfo */);
+        filterForLegacyLockdown(info);
         return info;
     }
 
@@ -1678,8 +1729,8 @@
                 result.put(
                         nai.network,
                         createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                nc, mDeps.getCallingUid(), callingPackageName,
-                                callingAttributionTag));
+                                nc, false /* includeLocationSensitiveInfo */,
+                                mDeps.getCallingUid(), callingPackageName, callingAttributionTag));
             }
         }
 
@@ -1692,7 +1743,9 @@
                     result.put(
                             network,
                             createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                    nc, mDeps.getCallingUid(), callingPackageName,
+                                    nc,
+                                    false /* includeLocationSensitiveInfo */,
+                                    mDeps.getCallingUid(), callingPackageName,
                                     callingAttributionTag));
                 }
             }
@@ -1774,6 +1827,7 @@
         enforceAccessPermission();
         return createWithLocationInfoSanitizedIfNecessaryWhenParceled(
                 getNetworkCapabilitiesInternal(network),
+                false /* includeLocationSensitiveInfo */,
                 mDeps.getCallingUid(), callingPackageName, callingAttributionTag);
     }
 
@@ -1807,8 +1861,8 @@
     @VisibleForTesting
     @Nullable
     NetworkCapabilities createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-            @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName,
-            @Nullable String callingAttributionTag) {
+            @Nullable NetworkCapabilities nc, boolean includeLocationSensitiveInfo,
+            int callerUid, @NonNull String callerPkgName, @Nullable String callingAttributionTag) {
         if (nc == null) {
             return null;
         }
@@ -1816,7 +1870,9 @@
         final NetworkCapabilities newNc;
         // Avoid doing location permission check if the transport info has no location sensitive
         // data.
-        if (nc.getTransportInfo() != null && nc.getTransportInfo().hasLocationSensitiveFields()) {
+        if (includeLocationSensitiveInfo
+                && nc.getTransportInfo() != null
+                && nc.getTransportInfo().hasLocationSensitiveFields()) {
             hasLocationPermission =
                     hasLocationPermission(callerUid, callerPkgName, callingAttributionTag);
             newNc = new NetworkCapabilities(nc, hasLocationPermission);
@@ -1833,6 +1889,16 @@
             // Owner UIDs already checked above. No need to re-check.
             return newNc;
         }
+        // If the caller does not want location sensitive data & target SDK >= S, then mask info.
+        // Else include the owner UID iff the caller has location permission to provide backwards
+        // compatibility for older apps.
+        if (!includeLocationSensitiveInfo
+                && isTargetSdkAtleast(
+                        Build.VERSION_CODES.S, callerUid, callerPkgName)) {
+            newNc.setOwnerUid(INVALID_UID);
+            return newNc;
+        }
+
         if (hasLocationPermission == null) {
             // Location permission not checked yet, check now for masking owner UID.
             hasLocationPermission =
@@ -2354,9 +2420,7 @@
         mContext.enforceCallingOrSelfPermission(KeepaliveTracker.PERMISSION, "ConnectivityService");
     }
 
-    // Public because it's used by mLockdownTracker.
-    public void sendConnectedBroadcast(NetworkInfo info) {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
+    private void sendConnectedBroadcast(NetworkInfo info) {
         sendGeneralBroadcast(info, CONNECTIVITY_ACTION);
     }
 
@@ -2593,7 +2657,7 @@
     @Override
     protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
             @Nullable String[] args) {
-        PriorityDump.dump(mPriorityDumper, fd, writer, args);
+        mPriorityDumper.dump(fd, writer, args);
     }
 
     private boolean checkDumpPermission(Context context, String tag, PrintWriter pw) {
@@ -2608,10 +2672,9 @@
         }
     }
 
-    private void doDump(FileDescriptor fd, PrintWriter writer, String[] args, boolean asProto) {
+    private void doDump(FileDescriptor fd, PrintWriter writer, String[] args) {
         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
         if (!checkDumpPermission(mContext, TAG, pw)) return;
-        if (asProto) return;
 
         if (CollectionUtils.contains(args, DIAG_ARG)) {
             dumpNetworkDiagnostics(pw);
@@ -2854,22 +2917,6 @@
             super(looper);
         }
 
-        private boolean maybeHandleAsyncChannelMessage(Message msg) {
-            switch (msg.what) {
-                default:
-                    return false;
-                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
-                    handleAsyncChannelHalfConnect(msg);
-                    break;
-                }
-                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
-                    handleAsyncChannelDisconnected(msg);
-                    break;
-                }
-            }
-            return true;
-        }
-
         private void maybeHandleNetworkAgentMessage(Message msg) {
             final Pair<NetworkAgentInfo, Object> arg = (Pair<NetworkAgentInfo, Object>) msg.obj;
             final NetworkAgentInfo nai = arg.first;
@@ -2910,7 +2957,7 @@
                     break;
                 }
                 case NetworkAgent.EVENT_NETWORK_SCORE_CHANGED: {
-                    updateNetworkScore(nai, msg.arg1);
+                    updateNetworkScore(nai, (NetworkScore) arg.second);
                     break;
                 }
                 case NetworkAgent.EVENT_SET_EXPLICITLY_SELECTED: {
@@ -3161,8 +3208,7 @@
 
         @Override
         public void handleMessage(Message msg) {
-            if (!maybeHandleAsyncChannelMessage(msg)
-                    && !maybeHandleNetworkMonitorMessage(msg)
+            if (!maybeHandleNetworkMonitorMessage(msg)
                     && !maybeHandleNetworkAgentInfoMessage(msg)) {
                 maybeHandleNetworkAgentMessage(msg);
             }
@@ -3426,21 +3472,6 @@
         return false;
     }
 
-    private void handleAsyncChannelHalfConnect(Message msg) {
-        ensureRunningOnConnectivityServiceThread();
-        if (mNetworkProviderInfos.containsKey(msg.replyTo)) {
-            if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
-                if (VDBG) log("NetworkFactory connected");
-                // Finish setting up the full connection
-                NetworkProviderInfo npi = mNetworkProviderInfos.get(msg.replyTo);
-                sendAllRequestsToProvider(npi);
-            } else {
-                loge("Error connecting NetworkFactory");
-                mNetworkProviderInfos.remove(msg.obj);
-            }
-        }
-    }
-
     private void handleNetworkAgentRegistered(Message msg) {
         final NetworkAgentInfo nai = (NetworkAgentInfo) msg.obj;
         if (!mNetworkAgentInfos.contains(nai)) {
@@ -3471,14 +3502,6 @@
         }
     }
 
-    // This is a no-op if it's called with a message designating a provider that has
-    // already been destroyed, because its reference will not be found in the relevant
-    // maps.
-    private void handleAsyncChannelDisconnected(Message msg) {
-        NetworkProviderInfo npi = mNetworkProviderInfos.remove(msg.replyTo);
-        if (DBG && npi != null) log("unregisterNetworkFactory for " + npi.name);
-    }
-
     // Destroys a network, remove references to it from the internal state managed by
     // ConnectivityService, free its interfaces and clean up.
     // Must be called on the Handler thread.
@@ -3604,11 +3627,10 @@
     // pendingIntent => NetworkRequestInfo map.
     // This method assumes that every non-null PendingIntent maps to exactly 1 NetworkRequestInfo.
     private NetworkRequestInfo findExistingNetworkRequestInfo(PendingIntent pendingIntent) {
-        Intent intent = pendingIntent.getIntent();
         for (Map.Entry<NetworkRequest, NetworkRequestInfo> entry : mNetworkRequests.entrySet()) {
             PendingIntent existingPendingIntent = entry.getValue().mPendingIntent;
             if (existingPendingIntent != null &&
-                    existingPendingIntent.getIntent().filterEquals(intent)) {
+                    existingPendingIntent.intentFilterEquals(pendingIntent)) {
                 return entry.getValue();
             }
         }
@@ -3642,6 +3664,7 @@
             mNetworkRequestInfoLogs.log("REGISTER " + nri);
             for (final NetworkRequest req : nri.mRequests) {
                 mNetworkRequests.put(req, nri);
+                // TODO: Consider update signal strength for other types.
                 if (req.isListen()) {
                     for (final NetworkAgentInfo network : mNetworkAgentInfos) {
                         if (req.networkCapabilities.hasSignalStrength()
@@ -3651,6 +3674,13 @@
                     }
                 }
             }
+            // If this NRI has a satisfier already, it is replacing an older request that
+            // has been removed. Track it.
+            final NetworkRequest activeRequest = nri.getActiveRequest();
+            if (null != activeRequest) {
+                // If there is an active request, then for sure there is a satisfier.
+                nri.getSatisfier().addRequest(activeRequest);
+            }
         }
 
         rematchAllNetworksAndRequests();
@@ -3727,18 +3757,19 @@
         // listen requests won't keep up a network satisfying it. If this is not a multilayer
         // request, return immediately. For multilayer requests, check to see if any of the
         // multilayer requests may have a potential satisfier.
-        if (!nri.isMultilayerRequest() && nri.mRequests.get(0).isListen()) {
+        if (!nri.isMultilayerRequest() && (nri.mRequests.get(0).isListen()
+                || nri.mRequests.get(0).isListenForBest())) {
             return false;
         }
         for (final NetworkRequest req : nri.mRequests) {
             // This multilayer listen request is satisfied therefore no further requests need to be
             // evaluated deeming this network not a potential satisfier.
-            if (req.isListen() && nri.getActiveRequest() == req) {
+            if ((req.isListen() || req.isListenForBest()) && nri.getActiveRequest() == req) {
                 return false;
             }
             // As non-multilayer listen requests have already returned, the below would only happen
             // for a multilayer request therefore continue to the next request if available.
-            if (req.isListen()) {
+            if (req.isListen() || req.isListenForBest()) {
                 continue;
             }
             // If this Network is already the highest scoring Network for a request, or if
@@ -3778,8 +3809,7 @@
                 ? mNetworkRequests.get(request) : getNriForAppRequest(request);
 
         if (nri != null) {
-            if (Process.SYSTEM_UID != callingUid && Process.NETWORK_STACK_UID != callingUid
-                    && nri.mUid != callingUid) {
+            if (Process.SYSTEM_UID != callingUid && nri.mUid != callingUid) {
                 log(String.format("UID %d attempted to %s for unowned request %s",
                         callingUid, requestedOperation, nri));
                 return null;
@@ -4404,7 +4434,8 @@
                     break;
                 }
                 case EVENT_PROXY_HAS_CHANGED: {
-                    handleApplyDefaultProxy((ProxyInfo)msg.obj);
+                    final Pair<Network, ProxyInfo> arg = (Pair<Network, ProxyInfo>) msg.obj;
+                    handleApplyDefaultProxy(arg.second);
                     break;
                 }
                 case EVENT_REGISTER_NETWORK_PROVIDER: {
@@ -4581,7 +4612,9 @@
         }
         mWakelockLogs.log("ACQUIRE for " + forWhom);
         Message msg = mHandler.obtainMessage(EVENT_EXPIRE_NET_TRANSITION_WAKELOCK);
-        mHandler.sendMessageDelayed(msg, mNetTransitionWakeLockTimeout);
+        final int lockTimeout = mResources.get().getInteger(
+                com.android.connectivity.resources.R.integer.config_networkTransitionTimeout);
+        mHandler.sendMessageDelayed(msg, lockTimeout);
     }
 
     // Called when we gain a new default network to release the network transition wakelock in a
@@ -4996,8 +5029,8 @@
         // The legacy lockdown VPN always uses the default network.
         // If the VPN's underlying network is no longer the current default network, it means that
         // the default network has just switched, and the VPN is about to disconnect.
-        // Report that the VPN is not connected, so when the state of NetworkInfo objects
-        // overwritten by getLegacyLockdownState will be set to CONNECTING and not CONNECTED.
+        // Report that the VPN is not connected, so the state of NetworkInfo objects overwritten
+        // by filterForLegacyLockdown will be set to CONNECTING and not CONNECTED.
         final NetworkAgentInfo defaultNetwork = getDefaultNetwork();
         if (defaultNetwork == null || !defaultNetwork.network.equals(underlying[0])) {
             return null;
@@ -5006,6 +5039,9 @@
         return nai;
     };
 
+    // TODO: move all callers to filterForLegacyLockdown and delete this method.
+    // This likely requires making sendLegacyNetworkBroadcast take a NetworkInfo object instead of
+    // just a DetailedState object.
     private DetailedState getLegacyLockdownState(DetailedState origState) {
         if (origState != DetailedState.CONNECTED) {
             return origState;
@@ -5015,6 +5051,23 @@
                 : DetailedState.CONNECTED;
     }
 
+    private void filterForLegacyLockdown(NetworkInfo ni) {
+        if (!mLockdownEnabled || !ni.isConnected()) return;
+        // The legacy lockdown VPN replaces the state of every network in CONNECTED state with the
+        // state of its VPN. This is to ensure that when an underlying network connects, apps will
+        // not see a CONNECTIVITY_ACTION broadcast for a network in state CONNECTED until the VPN
+        // comes up, at which point there is a new CONNECTIVITY_ACTION broadcast for the underlying
+        // network, this time with a state of CONNECTED.
+        //
+        // Now that the legacy lockdown code lives in ConnectivityService, and no longer has access
+        // to the internal state of the Vpn object, always replace the state with CONNECTING. This
+        // is not too far off the truth, since an always-on VPN, when not connected, is always
+        // trying to reconnect.
+        if (getLegacyLockdownNai() == null) {
+            ni.setDetailedState(DetailedState.CONNECTING, "", null);
+        }
+    }
+
     @Override
     public void setProvisioningNotificationVisible(boolean visible, int networkType,
             String action) {
@@ -5093,8 +5146,8 @@
         private final IBinder.DeathRecipient mDeathRecipient;
         public final int providerId;
 
-        NetworkProviderInfo(String name, Messenger messenger, AsyncChannel asyncChannel,
-                int providerId, @NonNull IBinder.DeathRecipient deathRecipient) {
+        NetworkProviderInfo(String name, Messenger messenger, int providerId,
+                @NonNull IBinder.DeathRecipient deathRecipient) {
             this.name = name;
             this.messenger = messenger;
             this.providerId = providerId;
@@ -5188,6 +5241,7 @@
         private final IBinder mBinder;
         final int mPid;
         final int mUid;
+        final @NetworkCallback.Flag int mCallbackFlags;
         @Nullable
         final String mCallingAttributionTag;
         // In order to preserve the mapping of NetworkRequest-to-callback when apps register
@@ -5235,17 +5289,26 @@
             mPid = getCallingPid();
             mUid = mDeps.getCallingUid();
             mNetworkRequestCounter.incrementCountOrThrow(mUid);
+            /**
+             * Location sensitive data not included in pending intent. Only included in
+             * {@link NetworkCallback}.
+             */
+            mCallbackFlags = NetworkCallback.FLAG_NONE;
             mCallingAttributionTag = callingAttributionTag;
         }
 
         NetworkRequestInfo(@NonNull final NetworkRequest r, @Nullable final Messenger m,
-                @Nullable final IBinder binder, @Nullable String callingAttributionTag) {
-            this(Collections.singletonList(r), r, m, binder, callingAttributionTag);
+                @Nullable final IBinder binder,
+                @NetworkCallback.Flag int callbackFlags,
+                @Nullable String callingAttributionTag) {
+            this(Collections.singletonList(r), r, m, binder, callbackFlags, callingAttributionTag);
         }
 
         NetworkRequestInfo(@NonNull final List<NetworkRequest> r,
                 @NonNull final NetworkRequest requestForCallback, @Nullable final Messenger m,
-                @Nullable final IBinder binder, @Nullable String callingAttributionTag) {
+                @Nullable final IBinder binder,
+                @NetworkCallback.Flag int callbackFlags,
+                @Nullable String callingAttributionTag) {
             super();
             ensureAllNetworkRequestsHaveType(r);
             mRequests = initializeRequests(r);
@@ -5256,6 +5319,7 @@
             mUid = mDeps.getCallingUid();
             mPendingIntent = null;
             mNetworkRequestCounter.incrementCountOrThrow(mUid);
+            mCallbackFlags = callbackFlags;
             mCallingAttributionTag = callingAttributionTag;
 
             try {
@@ -5271,20 +5335,33 @@
             ensureAllNetworkRequestsHaveType(r);
             mRequests = initializeRequests(r);
             mNetworkRequestForCallback = nri.getNetworkRequestForCallback();
-            // Note here that the satisfier may have corresponded to an old request, that
-            // this code doesn't try to take over. While it is a small discrepancy in the
-            // structure of these requests, it will be fixed by the next rematch and it's
-            // not as bad as having an NRI not storing its real satisfier.
-            // Fixing this discrepancy would require figuring out in the copying code what
-            // is the new request satisfied by this, which is a bit complex and not very
-            // useful as no code is using it until rematch fixes it.
-            mSatisfier = nri.mSatisfier;
+            final NetworkAgentInfo satisfier = nri.getSatisfier();
+            if (null != satisfier) {
+                // If the old NRI was satisfied by an NAI, then it may have had an active request.
+                // The active request is necessary to figure out what callbacks to send, in
+                // particular then a network updates its capabilities.
+                // As this code creates a new NRI with a new set of requests, figure out which of
+                // the list of requests should be the active request. It is always the first
+                // request of the list that can be satisfied by the satisfier since the order of
+                // requests is a priority order.
+                // Note even in the presence of a satisfier there may not be an active request,
+                // when the satisfier is the no-service network.
+                NetworkRequest activeRequest = null;
+                for (final NetworkRequest candidate : r) {
+                    if (candidate.canBeSatisfiedBy(satisfier.networkCapabilities)) {
+                        activeRequest = candidate;
+                        break;
+                    }
+                }
+                setSatisfier(satisfier, activeRequest);
+            }
             mMessenger = nri.mMessenger;
             mBinder = nri.mBinder;
             mPid = nri.mPid;
             mUid = nri.mUid;
             mPendingIntent = nri.mPendingIntent;
             mNetworkRequestCounter.incrementCountOrThrow(mUid);
+            mCallbackFlags = nri.mCallbackFlags;
             mCallingAttributionTag = nri.mCallingAttributionTag;
         }
 
@@ -5334,7 +5411,8 @@
                     + " callback request Id: "
                     + mNetworkRequestForCallback.requestId
                     + " " + mRequests
-                    + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
+                    + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent)
+                    + "callback flags: " + mCallbackFlags;
         }
     }
 
@@ -5418,13 +5496,13 @@
         }
     }
 
-    private boolean checkUnsupportedStartingFrom(int version, String callingPackageName) {
-        final UserHandle user = UserHandle.getUserHandleForUid(mDeps.getCallingUid());
+    private boolean isTargetSdkAtleast(int version, int callingUid,
+            @NonNull String callingPackageName) {
+        final UserHandle user = UserHandle.getUserHandleForUid(callingUid);
         final PackageManager pm =
                 mContext.createContextAsUser(user, 0 /* flags */).getPackageManager();
         try {
-            final int callingVersion = pm.getApplicationInfo(
-                    callingPackageName, 0 /* flags */).targetSdkVersion;
+            final int callingVersion = pm.getTargetSdkVersion(callingPackageName);
             if (callingVersion < version) return false;
         } catch (PackageManager.NameNotFoundException e) { }
         return true;
@@ -5433,10 +5511,11 @@
     @Override
     public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
             int reqTypeInt, Messenger messenger, int timeoutMs, IBinder binder,
-            int legacyType, @NonNull String callingPackageName,
+            int legacyType, int callbackFlags, @NonNull String callingPackageName,
             @Nullable String callingAttributionTag) {
         if (legacyType != TYPE_NONE && !checkNetworkStackPermission()) {
-            if (checkUnsupportedStartingFrom(Build.VERSION_CODES.M, callingPackageName)) {
+            if (isTargetSdkAtleast(Build.VERSION_CODES.M, mDeps.getCallingUid(),
+                    callingPackageName)) {
                 throw new SecurityException("Insufficient permissions to specify legacy type");
             }
         }
@@ -5473,6 +5552,10 @@
                 //  request if the app changes network state. http://b/29964605
                 enforceMeteredApnPolicy(networkCapabilities);
                 break;
+            case LISTEN_FOR_BEST:
+                enforceAccessPermission();
+                networkCapabilities = new NetworkCapabilities(networkCapabilities);
+                break;
             default:
                 throw new IllegalArgumentException("Unsupported request type " + reqType);
         }
@@ -5480,11 +5563,17 @@
         ensureSufficientPermissionsForRequest(networkCapabilities,
                 Binder.getCallingPid(), callingUid, callingPackageName);
 
-        // Set the UID range for this request to the single UID of the requester, or to an empty
-        // set of UIDs if the caller has the appropriate permission and UIDs have not been set.
+        // Enforce FOREGROUND if the caller does not have permission to use background network.
+        if (reqType == LISTEN_FOR_BEST) {
+            restrictBackgroundRequestForCaller(networkCapabilities);
+        }
+
+        // Set the UID range for this request to the single UID of the requester, unless the
+        // requester has the permission to specify other UIDs.
         // This will overwrite any allowed UIDs in the requested capabilities. Though there
         // are no visible methods to set the UIDs, an app could use reflection to try and get
         // networks for other apps so it's essential that the UIDs are overwritten.
+        // Also set the requester UID and package name in the request.
         restrictRequestUidsForCallerAndSetRequestorInfo(networkCapabilities,
                 callingUid, callingPackageName);
 
@@ -5496,7 +5585,7 @@
         final NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
                 nextNetworkRequestId(), reqType);
         final NetworkRequestInfo nri = getNriToRegister(
-                networkRequest, messenger, binder, callingAttributionTag);
+                networkRequest, messenger, binder, callbackFlags, callingAttributionTag);
         if (DBG) log("requestNetwork for " + nri);
 
         // For TRACK_SYSTEM_DEFAULT callbacks, the capabilities have been modified since they were
@@ -5531,6 +5620,7 @@
      */
     private NetworkRequestInfo getNriToRegister(@NonNull final NetworkRequest nr,
             @Nullable final Messenger msgr, @Nullable final IBinder binder,
+            @NetworkCallback.Flag int callbackFlags,
             @Nullable String callingAttributionTag) {
         final List<NetworkRequest> requests;
         if (NetworkRequest.Type.TRACK_DEFAULT == nr.type) {
@@ -5539,7 +5629,8 @@
         } else {
             requests = Collections.singletonList(nr);
         }
-        return new NetworkRequestInfo(requests, nr, msgr, binder, callingAttributionTag);
+        return new NetworkRequestInfo(
+                requests, nr, msgr, binder, callbackFlags, callingAttributionTag);
     }
 
     private void enforceNetworkRequestPermissions(NetworkCapabilities networkCapabilities,
@@ -5665,8 +5756,9 @@
 
     @Override
     public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
-            Messenger messenger, IBinder binder, @NonNull String callingPackageName,
-            @Nullable String callingAttributionTag) {
+            Messenger messenger, IBinder binder,
+            @NetworkCallback.Flag int callbackFlags,
+            @NonNull String callingPackageName, @NonNull String callingAttributionTag) {
         final int callingUid = mDeps.getCallingUid();
         if (!hasWifiNetworkListenPermission(networkCapabilities)) {
             enforceAccessPermission();
@@ -5687,7 +5779,8 @@
         NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
                 NetworkRequest.Type.LISTEN);
         NetworkRequestInfo nri =
-                new NetworkRequestInfo(networkRequest, messenger, binder, callingAttributionTag);
+                new NetworkRequestInfo(networkRequest, messenger, binder, callbackFlags,
+                        callingAttributionTag);
         if (VDBG) log("listenForNetwork for " + nri);
 
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
@@ -5756,8 +5849,7 @@
     public int registerNetworkProvider(Messenger messenger, String name) {
         enforceNetworkFactoryOrSettingsPermission();
         NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger,
-                null /* asyncChannel */, nextNetworkProviderId(),
-                () -> unregisterNetworkProvider(messenger));
+                nextNetworkProviderId(), () -> unregisterNetworkProvider(messenger));
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi));
         return npi.providerId;
     }
@@ -6008,20 +6100,6 @@
         return nai == getDefaultNetwork();
     }
 
-    // TODO : remove this method. It's a stopgap measure to help sheperding a number of dependent
-    // changes that would conflict throughout the automerger graph. Having this method temporarily
-    // helps with the process of going through with all these dependent changes across the entire
-    // tree.
-    /**
-     * Register a new agent. {@see #registerNetworkAgent} below.
-     */
-    public Network registerNetworkAgent(INetworkAgent na, NetworkInfo networkInfo,
-            LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
-            int currentScore, NetworkAgentConfig networkAgentConfig) {
-        return registerNetworkAgent(na, networkInfo, linkProperties, networkCapabilities,
-                currentScore, networkAgentConfig, NetworkProvider.ID_NONE);
-    }
-
     /**
      * Register a new agent with ConnectivityService to handle a network.
      *
@@ -6032,7 +6110,7 @@
      *         later : see {@link #updateLinkProperties}.
      * @param networkCapabilities the initial capabilites of this network. They can be updated
      *         later : see {@link #updateCapabilities}.
-     * @param currentScore the initial score of the network. See
+     * @param initialScore the initial score of the network. See
      *         {@link NetworkAgentInfo#getCurrentScore}.
      * @param networkAgentConfig metadata about the network. This is never updated.
      * @param providerId the ID of the provider owning this NetworkAgent.
@@ -6040,10 +6118,12 @@
      */
     public Network registerNetworkAgent(INetworkAgent na, NetworkInfo networkInfo,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
-            int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) {
+            @NonNull NetworkScore initialScore, NetworkAgentConfig networkAgentConfig,
+            int providerId) {
         Objects.requireNonNull(networkInfo, "networkInfo must not be null");
         Objects.requireNonNull(linkProperties, "linkProperties must not be null");
         Objects.requireNonNull(networkCapabilities, "networkCapabilities must not be null");
+        Objects.requireNonNull(initialScore, "initialScore must not be null");
         Objects.requireNonNull(networkAgentConfig, "networkAgentConfig must not be null");
         if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
             enforceAnyPermissionOf(Manifest.permission.MANAGE_TEST_NETWORKS);
@@ -6055,7 +6135,7 @@
         final long token = Binder.clearCallingIdentity();
         try {
             return registerNetworkAgentInternal(na, networkInfo, linkProperties,
-                    networkCapabilities, currentScore, networkAgentConfig, providerId, uid);
+                    networkCapabilities, initialScore, networkAgentConfig, providerId, uid);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -6063,7 +6143,8 @@
 
     private Network registerNetworkAgentInternal(INetworkAgent na, NetworkInfo networkInfo,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
-            int currentScore, NetworkAgentConfig networkAgentConfig, int providerId, int uid) {
+            NetworkScore currentScore, NetworkAgentConfig networkAgentConfig, int providerId,
+            int uid) {
         if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
             // Strictly, sanitizing here is unnecessary as the capabilities will be sanitized in
             // the call to mixInCapabilities below anyway, but sanitizing here means the NAI never
@@ -6078,7 +6159,7 @@
         final NetworkAgentInfo nai = new NetworkAgentInfo(na,
                 new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
                 currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig),
-                this, mNetd, mDnsResolver, providerId, uid, mQosCallbackTracker);
+                this, mNetd, mDnsResolver, providerId, uid, mQosCallbackTracker, mDeps);
 
         // Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says.
         processCapabilitiesFromAgent(nai, nc);
@@ -6281,13 +6362,13 @@
                 oldLp != null ? oldLp.getAllInterfaceNames() : null,
                 newLp != null ? newLp.getAllInterfaceNames() : null);
         if (!interfaceDiff.added.isEmpty()) {
-            final IBatteryStats bs = mDeps.getBatteryStatsService();
             for (final String iface : interfaceDiff.added) {
                 try {
                     if (DBG) log("Adding iface " + iface + " to network " + netId);
                     mNetd.networkAddInterface(netId, iface);
                     wakeupModifyInterface(iface, caps, true);
-                    bs.noteNetworkInterfaceForTransports(iface, caps.getTransportTypes());
+                    mDeps.reportNetworkInterfaceForTransports(mContext, iface,
+                            caps.getTransportTypes());
                 } catch (Exception e) {
                     loge("Exception adding interface: " + e);
                 }
@@ -6444,6 +6525,11 @@
     }
 
     private void updateWakeOnLan(@NonNull LinkProperties lp) {
+        if (mWolSupportedInterfaces == null) {
+            mWolSupportedInterfaces = new ArraySet<>(mResources.get().getStringArray(
+                    com.android.connectivity.resources.R.array
+                            .config_wakeonlan_supported_interfaces));
+        }
         lp.setWakeOnLanSupported(mWolSupportedInterfaces.contains(lp.getInterfaceName()));
     }
 
@@ -7016,6 +7102,8 @@
         if (notificationType != ConnectivityManager.CALLBACK_UNAVAIL) {
             putParcelable(bundle, networkAgent.network);
         }
+        final boolean includeLocationSensitiveInfo =
+                (nri.mCallbackFlags & NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) != 0;
         switch (notificationType) {
             case ConnectivityManager.CALLBACK_AVAILABLE: {
                 final NetworkCapabilities nc =
@@ -7024,7 +7112,8 @@
                 putParcelable(
                         bundle,
                         createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                nc, nri.mUid, nrForCallback.getRequestorPackageName(),
+                                nc, includeLocationSensitiveInfo, nri.mUid,
+                                nrForCallback.getRequestorPackageName(),
                                 nri.mCallingAttributionTag));
                 putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
                         networkAgent.linkProperties, nri.mPid, nri.mUid));
@@ -7044,7 +7133,8 @@
                 putParcelable(
                         bundle,
                         createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                netCap, nri.mUid, nrForCallback.getRequestorPackageName(),
+                                netCap, includeLocationSensitiveInfo, nri.mUid,
+                                nrForCallback.getRequestorPackageName(),
                                 nri.mCallingAttributionTag));
                 break;
             }
@@ -7778,7 +7868,7 @@
         }
     }
 
-    private void updateNetworkScore(@NonNull final NetworkAgentInfo nai, final int score) {
+    private void updateNetworkScore(@NonNull final NetworkAgentInfo nai, final NetworkScore score) {
         if (VDBG || DDBG) log("updateNetworkScore for " + nai.toShortString() + " to " + score);
         nai.setScore(score);
         rematchAllNetworksAndRequests();
@@ -7886,6 +7976,7 @@
         // and is still connected.
         NetworkInfo info = new NetworkInfo(nai.networkInfo);
         info.setType(type);
+        filterForLegacyLockdown(info);
         if (state != DetailedState.DISCONNECTED) {
             info.setDetailedState(state, null, info.getExtraInfo());
             sendConnectedBroadcast(info);
@@ -8000,8 +8091,8 @@
     @Override
     public String getCaptivePortalServerUrl() {
         enforceNetworkStackOrSettingsPermission();
-        String settingUrl = mContext.getResources().getString(
-                R.string.config_networkCaptivePortalServerUrl);
+        String settingUrl = mResources.get().getString(
+                com.android.connectivity.resources.R.string.config_networkCaptivePortalServerUrl);
 
         if (!TextUtils.isEmpty(settingUrl)) {
             return settingUrl;
diff --git a/services/core/java/com/android/server/ConnectivityServiceInitializer.java b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
index b992208..2465479 100644
--- a/services/core/java/com/android/server/ConnectivityServiceInitializer.java
+++ b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
@@ -16,9 +16,6 @@
 
 package com.android.server;
 
-import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
-import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
-
 import android.content.Context;
 import android.util.Log;
 
@@ -42,6 +39,6 @@
     public void onStart() {
         Log.i(TAG, "Registering " + Context.CONNECTIVITY_SERVICE);
         publishBinderService(Context.CONNECTIVITY_SERVICE, mConnectivity,
-                /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
+                /* allowIsolated= */ false);
     }
 }
diff --git a/services/core/java/com/android/server/NetIdManager.java b/services/core/java/com/android/server/NetIdManager.java
index 097fb3a..61925c8 100644
--- a/services/core/java/com/android/server/NetIdManager.java
+++ b/services/core/java/com/android/server/NetIdManager.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.annotation.NonNull;
+import android.net.ConnectivityManager;
 import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -31,7 +32,7 @@
     // Sequence number for Networks; keep in sync with system/netd/NetworkController.cpp
     public static final int MIN_NET_ID = 100; // some reserved marks
     // Top IDs reserved by IpSecService
-    public static final int MAX_NET_ID = 65535 - IpSecService.TUN_INTF_NETID_RANGE;
+    public static final int MAX_NET_ID = ConnectivityManager.getIpSecNetIdRange().getLower() - 1;
 
     @GuardedBy("mNetIdInUse")
     private final SparseBooleanArray mNetIdInUse = new SparseBooleanArray();
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index 55408ea..f566277 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -33,8 +33,8 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkProvider;
 import android.net.RouteInfo;
-import android.net.StringNetworkSpecifier;
 import android.net.TestNetworkInterface;
+import android.net.TestNetworkSpecifier;
 import android.net.util.NetdService;
 import android.os.Binder;
 import android.os.Handler;
@@ -90,7 +90,12 @@
         mCm = mContext.getSystemService(ConnectivityManager.class);
         mNetworkProvider = new NetworkProvider(mContext, mHandler.getLooper(),
                 TEST_NETWORK_PROVIDER_NAME);
-        mCm.registerNetworkProvider(mNetworkProvider);
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mCm.registerNetworkProvider(mNetworkProvider);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     /**
@@ -242,7 +247,7 @@
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
-        nc.setNetworkSpecifier(new StringNetworkSpecifier(iface));
+        nc.setNetworkSpecifier(new TestNetworkSpecifier(iface));
         nc.setAdministratorUids(administratorUids);
         if (!isMetered) {
             nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
diff --git a/services/core/java/com/android/server/connectivity/ConnectivityResources.java b/services/core/java/com/android/server/connectivity/ConnectivityResources.java
new file mode 100644
index 0000000..45cf21e
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/ConnectivityResources.java
@@ -0,0 +1,83 @@
+/*
+ * 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 static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.util.Log;
+
+import com.android.server.ConnectivityService;
+
+import java.util.List;
+
+/**
+ * Utility to obtain the {@link ConnectivityService} {@link Resources}, in the
+ * ServiceConnectivityResources APK.
+ */
+public class ConnectivityResources {
+    private static final String RESOURCES_APK_INTENT =
+            "com.android.server.connectivity.intent.action.SERVICE_CONNECTIVITY_RESOURCES_APK";
+    private static final String RES_PKG_DIR = "/apex/com.android.tethering/";
+
+    @NonNull
+    private final Context mContext;
+
+    @Nullable
+    private Resources mResources = null;
+
+    public ConnectivityResources(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Get the {@link Resources} of the ServiceConnectivityResources APK.
+     */
+    public synchronized Resources get() {
+        if (mResources != null) {
+            return mResources;
+        }
+
+        final List<ResolveInfo> pkgs = mContext.getPackageManager()
+                .queryIntentActivities(new Intent(RESOURCES_APK_INTENT), MATCH_SYSTEM_ONLY);
+        pkgs.removeIf(pkg -> !pkg.activityInfo.applicationInfo.sourceDir.startsWith(RES_PKG_DIR));
+        if (pkgs.size() > 1) {
+            Log.wtf(ConnectivityResources.class.getSimpleName(),
+                    "More than one package found: " + pkgs);
+        }
+        if (pkgs.isEmpty()) {
+            throw new IllegalStateException("No connectivity resource package found");
+        }
+
+        final Context pkgContext;
+        try {
+            pkgContext = mContext.createPackageContext(
+                    pkgs.get(0).activityInfo.applicationInfo.packageName, 0 /* flags */);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalStateException("Resolved package not found", e);
+        }
+
+        mResources = pkgContext.getResources();
+        return mResources;
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index 4f6b530..7a5abf8 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -16,7 +16,6 @@
 
 package com.android.server.connectivity;
 
-import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
 import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_FAILURE;
@@ -33,6 +32,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.net.ConnectivityManager;
 import android.net.IDnsResolver;
 import android.net.InetAddresses;
 import android.net.LinkProperties;
@@ -128,7 +128,7 @@
     private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
 
     public static PrivateDnsConfig getPrivateDnsConfig(ContentResolver cr) {
-        final String mode = getPrivateDnsMode(cr);
+        final String mode = ConnectivityManager.getPrivateDnsMode(cr);
 
         final boolean useTls = !TextUtils.isEmpty(mode) && !PRIVATE_DNS_MODE_OFF.equals(mode);
 
@@ -479,13 +479,6 @@
         return result;
     }
 
-    private static String getPrivateDnsMode(ContentResolver cr) {
-        String mode = getStringSetting(cr, PRIVATE_DNS_MODE);
-        if (TextUtils.isEmpty(mode)) mode = getStringSetting(cr, PRIVATE_DNS_DEFAULT_MODE);
-        if (TextUtils.isEmpty(mode)) mode = PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
-        return mode;
-    }
-
     private static String getStringSetting(ContentResolver cr, String which) {
         return Settings.Global.getString(cr, which);
     }
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index fa80b25..c66a280 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -16,6 +16,8 @@
 
 package com.android.server.connectivity;
 
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+
 import static com.android.net.module.util.CollectionUtils.contains;
 
 import android.annotation.NonNull;
@@ -35,6 +37,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.net.module.util.NetworkStackConstants;
+import com.android.server.ConnectivityService;
 
 import java.net.Inet6Address;
 import java.util.Objects;
@@ -94,12 +97,15 @@
     private Inet6Address mIPv6Address;
     private State mState = State.IDLE;
 
+    private boolean mEnableClatOnCellular;
     private boolean mPrefixDiscoveryRunning;
 
-    public Nat464Xlat(NetworkAgentInfo nai, INetd netd, IDnsResolver dnsResolver) {
+    public Nat464Xlat(NetworkAgentInfo nai, INetd netd, IDnsResolver dnsResolver,
+            ConnectivityService.Dependencies deps) {
         mDnsResolver = dnsResolver;
         mNetd = netd;
         mNetwork = nai;
+        mEnableClatOnCellular = deps.getCellular464XlatEnabled();
     }
 
     /**
@@ -111,7 +117,7 @@
      * @return true if the network requires clat, false otherwise.
      */
     @VisibleForTesting
-    protected static boolean requiresClat(NetworkAgentInfo nai) {
+    protected boolean requiresClat(NetworkAgentInfo nai) {
         // TODO: migrate to NetworkCapabilities.TRANSPORT_*.
         final boolean supported = contains(NETWORK_TYPES, nai.networkInfo.getType());
         final boolean connected = contains(NETWORK_STATES, nai.networkInfo.getState());
@@ -126,7 +132,9 @@
         final boolean skip464xlat = (nai.netAgentConfig() != null)
                 && nai.netAgentConfig().skip464xlat;
 
-        return supported && connected && isIpv6OnlyNetwork && !skip464xlat;
+        return supported && connected && isIpv6OnlyNetwork && !skip464xlat
+            && (nai.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)
+                ? isCellular464XlatEnabled() : true);
     }
 
     /**
@@ -137,7 +145,7 @@
      * @return true if the network should start clat, false otherwise.
      */
     @VisibleForTesting
-    protected static boolean shouldStartClat(NetworkAgentInfo nai) {
+    protected boolean shouldStartClat(NetworkAgentInfo nai) {
         LinkProperties lp = nai.linkProperties;
         return requiresClat(nai) && lp != null && lp.getNat64Prefix() != null;
     }
@@ -507,4 +515,9 @@
     protected int getNetId() {
         return mNetwork.network.getNetId();
     }
+
+    @VisibleForTesting
+    protected boolean isCellular464XlatEnabled() {
+        return mEnableClatOnCellular;
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 1d0e115..e44dcf5 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -35,6 +35,7 @@
 import android.net.NetworkInfo;
 import android.net.NetworkMonitorManager;
 import android.net.NetworkRequest;
+import android.net.NetworkScore;
 import android.net.NetworkStateSnapshot;
 import android.net.QosCallbackException;
 import android.net.QosFilter;
@@ -302,8 +303,8 @@
     // validated).
     private boolean mInactive;
 
-    // This represents the quality of the network with no clear scale.
-    private int mScore;
+    // This represents the quality of the network.
+    private NetworkScore mScore;
 
     // The list of NetworkRequests being satisfied by this Network.
     private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>();
@@ -338,10 +339,11 @@
     private final QosCallbackTracker mQosCallbackTracker;
 
     public NetworkAgentInfo(INetworkAgent na, Network net, NetworkInfo info,
-            @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc, int score, Context context,
+            @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc,
+            @NonNull NetworkScore score, Context context,
             Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
             IDnsResolver dnsResolver, int factorySerialNumber, int creatorUid,
-            QosCallbackTracker qosCallbackTracker) {
+            QosCallbackTracker qosCallbackTracker, ConnectivityService.Dependencies deps) {
         Objects.requireNonNull(net);
         Objects.requireNonNull(info);
         Objects.requireNonNull(lp);
@@ -355,7 +357,7 @@
         linkProperties = lp;
         networkCapabilities = nc;
         mScore = score;
-        clatd = new Nat464Xlat(this, netd, dnsResolver);
+        clatd = new Nat464Xlat(this, netd, dnsResolver, deps);
         mConnService = connService;
         mContext = context;
         mHandler = handler;
@@ -603,9 +605,9 @@
         }
 
         @Override
-        public void sendScore(int score) {
-            mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_SCORE_CHANGED, score, 0,
-                    new Pair<>(NetworkAgentInfo.this, null)).sendToTarget();
+        public void sendScore(@NonNull final NetworkScore score) {
+            mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_SCORE_CHANGED,
+                    new Pair<>(NetworkAgentInfo.this, score)).sendToTarget();
         }
 
         @Override
@@ -717,6 +719,7 @@
                 break;
 
             case LISTEN:
+            case LISTEN_FOR_BEST:
             case TRACK_DEFAULT:
             case TRACK_SYSTEM_DEFAULT:
                 break;
@@ -857,7 +860,7 @@
             return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE;
         }
 
-        int score = mScore;
+        int score = mScore.getLegacyInt();
         if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) {
             score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY;
         }
@@ -886,7 +889,7 @@
         return getCurrentScore(true);
     }
 
-    public void setScore(final int score) {
+    public void setScore(final NetworkScore score) {
         mScore = score;
     }
 
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 9411e33..488677a 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -31,14 +31,17 @@
 import static com.android.net.module.util.CollectionUtils.toIntArray;
 
 import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PackageManagerInternal;
 import android.net.INetd;
 import android.net.UidRange;
+import android.net.Uri;
 import android.os.Build;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
@@ -54,7 +57,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.net.module.util.CollectionUtils;
-import com.android.server.LocalServices;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -71,7 +73,7 @@
  *
  * @hide
  */
-public class PermissionMonitor implements PackageManagerInternal.PackageListObserver {
+public class PermissionMonitor {
     private static final String TAG = "PermissionMonitor";
     private static final boolean DBG = true;
     protected static final Boolean SYSTEM = Boolean.TRUE;
@@ -83,6 +85,7 @@
     private final SystemConfigManager mSystemConfigManager;
     private final INetd mNetd;
     private final Dependencies mDeps;
+    private final Context mContext;
 
     @GuardedBy("this")
     private final Set<UserHandle> mUsers = new HashSet<>();
@@ -102,6 +105,25 @@
     @GuardedBy("this")
     private final Set<Integer> mAllApps = new HashSet<>();
 
+    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+            final Uri packageData = intent.getData();
+            final String packageName =
+                    packageData != null ? packageData.getSchemeSpecificPart() : null;
+
+            if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                onPackageAdded(packageName, uid);
+            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                onPackageRemoved(packageName, uid);
+            } else {
+                Log.wtf(TAG, "received unexpected intent: " + action);
+            }
+        }
+    };
+
     /**
      * Dependencies of PermissionMonitor, for injection in tests.
      */
@@ -127,6 +149,7 @@
         mSystemConfigManager = context.getSystemService(SystemConfigManager.class);
         mNetd = netd;
         mDeps = deps;
+        mContext = context;
     }
 
     // Intended to be called only once at startup, after the system is ready. Installs a broadcast
@@ -134,12 +157,14 @@
     public synchronized void startMonitoring() {
         log("Monitoring");
 
-        PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
-        if (pmi != null) {
-            pmi.getPackageList(this);
-        } else {
-            loge("failed to get the PackageManagerInternal service");
-        }
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        intentFilter.addDataScheme("package");
+        mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */).registerReceiver(
+                mIntentReceiver, intentFilter, null /* broadcastPermission */,
+                null /* scheduler */);
+
         List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS
                 | MATCH_ANY_USER);
         if (apps == null) {
@@ -347,9 +372,10 @@
      *
      * @hide
      */
-    @Override
     public synchronized void onPackageAdded(@NonNull final String packageName, final int uid) {
-        sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
+        // TODO: Netd is using appId for checking traffic permission. Correct the methods that are
+        //  using appId instead of uid actually
+        sendPackagePermissionsForUid(UserHandle.getAppId(uid), getPermissionForUid(uid));
 
         // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
         // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
@@ -384,9 +410,10 @@
      *
      * @hide
      */
-    @Override
     public synchronized void onPackageRemoved(@NonNull final String packageName, final int uid) {
-        sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
+        // TODO: Netd is using appId for checking traffic permission. Correct the methods that are
+        //  using appId instead of uid actually
+        sendPackagePermissionsForUid(UserHandle.getAppId(uid), getPermissionForUid(uid));
 
         // If the newly-removed package falls within some VPN's uid range, update Netd with it.
         // This needs to happen before the mApps update below, since removeBypassingUids() depends
@@ -432,19 +459,6 @@
         }
     }
 
-    /**
-     * Called when a package is changed.
-     *
-     * @param packageName The name of the changed package.
-     * @param uid The uid of the changed package.
-     *
-     * @hide
-     */
-    @Override
-    public synchronized void onPackageChanged(@NonNull final String packageName, final int uid) {
-        sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
-    }
-
     private static int getNetdPermissionMask(String[] requestedPermissions,
                                              int[] requestedPermissionsFlags) {
         int permissions = 0;
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index d83ff83..8b9c836 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -27,15 +27,19 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.net.Network;
+import android.net.PacProxyManager;
 import android.net.Proxy;
 import android.net.ProxyInfo;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.net.module.util.ProxyUtils;
@@ -67,7 +71,7 @@
     // is not set. Individual networks have their own settings that override this. This member
     // is set through setDefaultProxy, which is called when the default network changes proxies
     // in its LinkProperties, or when ConnectivityService switches to a new default network, or
-    // when PacProxyInstaller resolves the proxy.
+    // when PacProxyService resolves the proxy.
     @Nullable
     @GuardedBy("mProxyLock")
     private volatile ProxyInfo mDefaultProxy = null;
@@ -77,16 +81,31 @@
 
     private final Handler mConnectivityServiceHandler;
 
-    // The object responsible for Proxy Auto Configuration (PAC).
-    @NonNull
-    private final PacProxyInstaller mPacProxyInstaller;
+    private final PacProxyManager mPacProxyManager;
+
+    private class PacProxyInstalledListener implements PacProxyManager.PacProxyInstalledListener {
+        private final int mEvent;
+
+        PacProxyInstalledListener(int event) {
+            mEvent = event;
+        }
+
+        public void onPacProxyInstalled(@Nullable Network network, @NonNull ProxyInfo proxy) {
+            mConnectivityServiceHandler
+                    .sendMessage(mConnectivityServiceHandler
+                    .obtainMessage(mEvent, new Pair<>(network, proxy)));
+        }
+    }
 
     public ProxyTracker(@NonNull final Context context,
             @NonNull final Handler connectivityServiceInternalHandler, final int pacChangedEvent) {
         mContext = context;
         mConnectivityServiceHandler = connectivityServiceInternalHandler;
-        mPacProxyInstaller = new PacProxyInstaller(
-                context, connectivityServiceInternalHandler, pacChangedEvent);
+        mPacProxyManager = context.getSystemService(PacProxyManager.class);
+
+        PacProxyInstalledListener listener = new PacProxyInstalledListener(pacChangedEvent);
+        mPacProxyManager.addPacProxyInstalledListener(
+                new HandlerExecutor(mConnectivityServiceHandler), listener);
     }
 
     // Convert empty ProxyInfo's to null as null-checks are used to determine if proxies are present
@@ -182,7 +201,7 @@
 
             if (!TextUtils.isEmpty(pacFileUrl)) {
                 mConnectivityServiceHandler.post(
-                        () -> mPacProxyInstaller.setCurrentProxyScriptUrl(proxyProperties));
+                        () -> mPacProxyManager.setCurrentProxyScriptUrl(proxyProperties));
             }
         }
     }
@@ -226,9 +245,9 @@
         final ProxyInfo defaultProxy = getDefaultProxy();
         final ProxyInfo proxyInfo = null != defaultProxy ?
                 defaultProxy : ProxyInfo.buildDirectProxy("", 0, Collections.emptyList());
+        mPacProxyManager.setCurrentProxyScriptUrl(proxyInfo);
 
-        if (mPacProxyInstaller.setCurrentProxyScriptUrl(proxyInfo)
-                == PacProxyInstaller.DONT_SEND_BROADCAST) {
+        if (!shouldSendBroadcast(proxyInfo)) {
             return;
         }
         if (DBG) Log.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
@@ -244,6 +263,10 @@
         }
     }
 
+    private boolean shouldSendBroadcast(ProxyInfo proxy) {
+        return Uri.EMPTY.equals(proxy.getPacFileUrl()) || proxy.getPort() > 0;
+    }
+
     /**
      * Sets the global proxy in memory. Also writes the values to the global settings of the device.
      *
@@ -308,10 +331,10 @@
                 return;
             }
 
-            // This call could be coming from the PacProxyInstaller, containing the port of the
+            // This call could be coming from the PacProxyService, containing the port of the
             // local proxy. If this new proxy matches the global proxy then copy this proxy to the
             // global (to get the correct local port), and send a broadcast.
-            // TODO: Switch PacProxyInstaller to have its own message to send back rather than
+            // TODO: Switch PacProxyService to have its own message to send back rather than
             // reusing EVENT_HAS_CHANGED_PROXY and this call to handleApplyDefaultProxy.
             if ((mGlobalProxy != null) && (proxyInfo != null)
                     && (!Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index 8122495..e1a424f 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -70,7 +70,7 @@
         "mockito-target-minus-junit4",
         "net-tests-utils",
         "platform-test-annotations",
-        "service-connectivity",
+        "service-connectivity-pre-jarjar",
         "services.core",
         "services.net",
     ],
@@ -79,6 +79,7 @@
         "android.test.runner",
         "android.test.base",
         "android.test.mock",
+        "ServiceConnectivityResources",
     ],
     jni_libs: [
         "libservice-connectivity",
diff --git a/tests/net/TEST_MAPPING b/tests/net/TEST_MAPPING
index 89fc6ea..d659688 100644
--- a/tests/net/TEST_MAPPING
+++ b/tests/net/TEST_MAPPING
@@ -9,6 +9,23 @@
       "name": "FrameworksNetDeflakeTest"
     }
   ],
+  "auto-postsubmit": [
+    // Test tag for automotive targets. These are only running in postsubmit so as to harden the
+    // automotive targets to avoid introducing additional test flake and build time. The plan for
+    // presubmit testing for auto is to augment the existing tests to cover auto use cases as well.
+    // Additionally, this tag is used in targeted test suites to limit resource usage on the test
+    // infra during the hardening phase.
+    // TODO: this tag to be removed once the above is no longer an issue.
+    {
+      "name": "FrameworksNetTests"
+    },
+    {
+      "name": "FrameworksNetIntegrationTests"
+    },
+    {
+      "name": "FrameworksNetDeflakeTest"
+    }
+  ],
   "imports": [
     {
       "path": "cts/tests/tests/net"
diff --git a/tests/net/common/java/android/net/IpPrefixTest.java b/tests/net/common/java/android/net/IpPrefixTest.java
index 9c0fc7c..50ecb42 100644
--- a/tests/net/common/java/android/net/IpPrefixTest.java
+++ b/tests/net/common/java/android/net/IpPrefixTest.java
@@ -113,6 +113,15 @@
             p = new IpPrefix("f00:::/32");
             fail("Expected IllegalArgumentException: invalid IPv6 address");
         } catch (IllegalArgumentException expected) { }
+
+        p = new IpPrefix("/64");
+        assertEquals("::/64", p.toString());
+
+        p = new IpPrefix("/128");
+        assertEquals("::1/128", p.toString());
+
+        p = new IpPrefix("[2001:db8::123]/64");
+        assertEquals("2001:db8::/64", p.toString());
     }
 
     @Test
diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/tests/net/common/java/android/net/LinkAddressTest.java
index 1eaf30c..2cf3cf9 100644
--- a/tests/net/common/java/android/net/LinkAddressTest.java
+++ b/tests/net/common/java/android/net/LinkAddressTest.java
@@ -53,6 +53,7 @@
 import org.junit.runner.RunWith;
 
 import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.InterfaceAddress;
 import java.net.NetworkInterface;
@@ -117,6 +118,20 @@
         assertEquals(456, address.getScope());
         assertTrue(address.isIpv4());
 
+        address = new LinkAddress("/64", 1 /* flags */, 2 /* scope */);
+        assertEquals(Inet6Address.LOOPBACK, address.getAddress());
+        assertEquals(64, address.getPrefixLength());
+        assertEquals(1, address.getFlags());
+        assertEquals(2, address.getScope());
+        assertTrue(address.isIpv6());
+
+        address = new LinkAddress("[2001:db8::123]/64", 3 /* flags */, 4 /* scope */);
+        assertEquals(InetAddresses.parseNumericAddress("2001:db8::123"), address.getAddress());
+        assertEquals(64, address.getPrefixLength());
+        assertEquals(3, address.getFlags());
+        assertEquals(4, address.getScope());
+        assertTrue(address.isIpv6());
+
         // InterfaceAddress doesn't have a constructor. Fetch some from an interface.
         List<InterfaceAddress> addrs = NetworkInterface.getByName("lo").getInterfaceAddresses();
 
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 5d0e016..0dfec75 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -28,6 +28,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE;
@@ -44,6 +45,10 @@
 import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES;
 import static android.os.Process.INVALID_UID;
 
+import static com.android.modules.utils.build.SdkLevel.isAtLeastR;
+import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
+import static com.android.testutils.MiscAsserts.assertEmpty;
+import static com.android.testutils.MiscAsserts.assertThrows;
 import static com.android.testutils.ParcelUtils.assertParcelSane;
 import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
 import static com.android.testutils.ParcelUtils.parcelingRoundTrip;
@@ -67,7 +72,7 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.modules.utils.build.SdkLevel;
+import com.android.testutils.CompatUtil;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 
@@ -84,6 +89,9 @@
 public class NetworkCapabilitiesTest {
     private static final String TEST_SSID = "TEST_SSID";
     private static final String DIFFERENT_TEST_SSID = "DIFFERENT_TEST_SSID";
+    private static final int TEST_SUBID1 = 1;
+    private static final int TEST_SUBID2 = 2;
+    private static final int TEST_SUBID3 = 3;
 
     @Rule
     public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule();
@@ -91,14 +99,6 @@
     private DiscoverySession mDiscoverySession = Mockito.mock(DiscoverySession.class);
     private PeerHandle mPeerHandle = Mockito.mock(PeerHandle.class);
 
-    private boolean isAtLeastR() {
-        return SdkLevel.isAtLeastR();
-    }
-
-    private boolean isAtLeastS() {
-        return SdkLevel.isAtLeastS();
-    }
-
     @Test
     public void testMaybeMarkCapabilitiesRestricted() {
         // verify EIMS is restricted
@@ -211,7 +211,7 @@
         nc1 = new NetworkCapabilities().addTransportType(TRANSPORT_WIFI);
         nc2 = new NetworkCapabilities()
                 .addTransportType(TRANSPORT_WIFI)
-                .setNetworkSpecifier(new StringNetworkSpecifier("specs"));
+                .setNetworkSpecifier(CompatUtil.makeEthernetNetworkSpecifier("eth42"));
         assertNotEquals("", nc1.describeImmutableDifferences(nc2));
         assertEquals("", nc1.describeImmutableDifferences(nc1));
     }
@@ -304,7 +304,9 @@
             .setUids(uids)
             .addCapability(NET_CAPABILITY_EIMS)
             .addCapability(NET_CAPABILITY_NOT_METERED);
-        if (isAtLeastR()) {
+        if (isAtLeastS()) {
+            netCap.setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2));
+        } else if (isAtLeastR()) {
             netCap.setOwnerUid(123);
             netCap.setAdministratorUids(new int[] {5, 11});
         }
@@ -379,7 +381,7 @@
 
     private void testParcelSane(NetworkCapabilities cap) {
         if (isAtLeastS()) {
-            assertParcelSane(cap, 16);
+            assertParcelSane(cap, 17);
         } else if (isAtLeastR()) {
             assertParcelSane(cap, 15);
         } else {
@@ -613,6 +615,20 @@
         assertFalse(nc2.appliesToUid(12));
         assertTrue(nc1.appliesToUid(22));
         assertTrue(nc2.appliesToUid(22));
+
+        // Verify the subscription id list can be combined only when they are equal.
+        if (isAtLeastS()) {
+            nc1.setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2));
+            nc2.setSubIds(Set.of(TEST_SUBID2));
+            assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1));
+
+            nc2.setSubIds(Set.of());
+            assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1));
+
+            nc2.setSubIds(Set.of(TEST_SUBID2, TEST_SUBID1));
+            nc2.combineCapabilities(nc1);
+            assertEquals(Set.of(TEST_SUBID2, TEST_SUBID1), nc2.getSubIds());
+        }
     }
 
     @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
@@ -671,7 +687,7 @@
         NetworkCapabilities nc1 = new NetworkCapabilities();
         nc1.addTransportType(TRANSPORT_CELLULAR).addTransportType(TRANSPORT_WIFI);
         try {
-            nc1.setNetworkSpecifier(new StringNetworkSpecifier("specs"));
+            nc1.setNetworkSpecifier(CompatUtil.makeEthernetNetworkSpecifier("eth0"));
             fail("Cannot set NetworkSpecifier on a NetworkCapability with multiple transports!");
         } catch (IllegalStateException expected) {
             // empty
@@ -680,7 +696,7 @@
         // Sequence 2: Transport + NetworkSpecifier + Transport
         NetworkCapabilities nc2 = new NetworkCapabilities();
         nc2.addTransportType(TRANSPORT_CELLULAR).setNetworkSpecifier(
-                new StringNetworkSpecifier("specs"));
+                CompatUtil.makeEthernetNetworkSpecifier("testtap3"));
         try {
             nc2.addTransportType(TRANSPORT_WIFI);
             fail("Cannot set a second TransportType of a network which has a NetworkSpecifier!");
@@ -761,6 +777,24 @@
         nc1.setUids(uidRange(10, 13));
         nc2.set(nc1);  // Overwrites, as opposed to combineCapabilities
         assertEquals(nc1, nc2);
+
+        if (isAtLeastS()) {
+            assertThrows(NullPointerException.class, () -> nc1.setSubIds(null));
+            nc1.setSubIds(Set.of());
+            nc2.set(nc1);
+            assertEquals(nc1, nc2);
+
+            nc1.setSubIds(Set.of(TEST_SUBID1));
+            nc2.set(nc1);
+            assertEquals(nc1, nc2);
+
+            nc2.setSubIds(Set.of(TEST_SUBID2, TEST_SUBID1));
+            nc2.set(nc1);
+            assertEquals(nc1, nc2);
+
+            nc2.setSubIds(Set.of(TEST_SUBID3, TEST_SUBID2));
+            assertNotEquals(nc1, nc2);
+        }
     }
 
     @Test
@@ -841,6 +875,50 @@
         } catch (NullPointerException expected) { }
     }
 
+    private static NetworkCapabilities capsWithSubIds(Integer ... subIds) {
+        // Since the NetworkRequest would put NOT_VCN_MANAGED capabilities in general, for
+        // every NetworkCapabilities that simulates networks needs to add it too in order to
+        // satisfy these requests.
+        final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+                .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+                .setSubIds(new ArraySet<>(subIds)).build();
+        assertEquals(new ArraySet<>(subIds), nc.getSubIds());
+        return nc;
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+    public void testSubIds() throws Exception {
+        final NetworkCapabilities ncWithoutId = capsWithSubIds();
+        final NetworkCapabilities ncWithId = capsWithSubIds(TEST_SUBID1);
+        final NetworkCapabilities ncWithOtherIds = capsWithSubIds(TEST_SUBID1, TEST_SUBID3);
+        final NetworkCapabilities ncWithoutRequestedIds = capsWithSubIds(TEST_SUBID3);
+
+        final NetworkRequest requestWithoutId = new NetworkRequest.Builder().build();
+        assertEmpty(requestWithoutId.networkCapabilities.getSubIds());
+        final NetworkRequest requestWithIds = new NetworkRequest.Builder()
+                .setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2)).build();
+        assertEquals(Set.of(TEST_SUBID1, TEST_SUBID2),
+                requestWithIds.networkCapabilities.getSubIds());
+
+        assertFalse(requestWithIds.canBeSatisfiedBy(ncWithoutId));
+        assertTrue(requestWithIds.canBeSatisfiedBy(ncWithOtherIds));
+        assertFalse(requestWithIds.canBeSatisfiedBy(ncWithoutRequestedIds));
+        assertTrue(requestWithIds.canBeSatisfiedBy(ncWithId));
+        assertTrue(requestWithoutId.canBeSatisfiedBy(ncWithoutId));
+        assertTrue(requestWithoutId.canBeSatisfiedBy(ncWithId));
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+    public void testEqualsSubIds() throws Exception {
+        assertEquals(capsWithSubIds(), capsWithSubIds());
+        assertNotEquals(capsWithSubIds(), capsWithSubIds(TEST_SUBID1));
+        assertEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID1));
+        assertNotEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID2));
+        assertNotEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID2, TEST_SUBID1));
+        assertEquals(capsWithSubIds(TEST_SUBID1, TEST_SUBID2),
+                capsWithSubIds(TEST_SUBID2, TEST_SUBID1));
+    }
+
     @Test
     public void testLinkBandwidthKbps() {
         final NetworkCapabilities nc = new NetworkCapabilities();
@@ -1021,5 +1099,11 @@
             fail("Should not set null into NetworkCapabilities.Builder");
         } catch (NullPointerException expected) { }
         assertEquals(nc, new NetworkCapabilities.Builder(nc).build());
+
+        if (isAtLeastS()) {
+            final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
+                    .setSubIds(Set.of(TEST_SUBID1)).build();
+            assertEquals(Set.of(TEST_SUBID1), nc2.getSubIds());
+        }
     }
 }
diff --git a/tests/net/common/java/android/net/NetworkProviderTest.kt b/tests/net/common/java/android/net/NetworkProviderTest.kt
index bcc9072..340e6f9 100644
--- a/tests/net/common/java/android/net/NetworkProviderTest.kt
+++ b/tests/net/common/java/android/net/NetworkProviderTest.kt
@@ -27,6 +27,7 @@
 import android.os.Looper
 import androidx.test.InstrumentationRegistry
 import com.android.net.module.util.ArrayTrackRecord
+import com.android.testutils.CompatUtil
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
 import com.android.testutils.DevSdkIgnoreRunner
 import com.android.testutils.isDevSdkInRange
@@ -102,7 +103,8 @@
         mCm.registerNetworkProvider(provider)
         assertNotEquals(provider.getProviderId(), NetworkProvider.ID_NONE)
 
-        val specifier = StringNetworkSpecifier(UUID.randomUUID().toString())
+        val specifier = CompatUtil.makeTestNetworkSpecifier(
+                UUID.randomUUID().toString())
         val nr: NetworkRequest = NetworkRequest.Builder()
                 .addTransportType(TRANSPORT_TEST)
                 .setNetworkSpecifier(specifier)
@@ -183,7 +185,8 @@
 
         mCm.registerNetworkProvider(provider)
 
-        val specifier = StringNetworkSpecifier(UUID.randomUUID().toString())
+        val specifier = CompatUtil.makeTestNetworkSpecifier(
+                UUID.randomUUID().toString())
         val nr: NetworkRequest = NetworkRequest.Builder()
                 .addTransportType(TRANSPORT_TEST)
                 .setNetworkSpecifier(specifier)
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index e1da3d0..01d8186 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -64,6 +64,7 @@
     private final HandlerThread mHandlerThread;
     private final Context mContext;
     private final String mLogTag;
+    private final NetworkAgentConfig mNetworkAgentConfig;
 
     private final ConditionVariable mDisconnected = new ConditionVariable();
     private final ConditionVariable mPreventReconnectReceived = new ConditionVariable();
@@ -115,13 +116,19 @@
         mHandlerThread = new HandlerThread(mLogTag);
         mHandlerThread.start();
 
-        mNetworkAgent = makeNetworkAgent(linkProperties, type, typeName);
+        // extraInfo is set to "" by default in NetworkAgentConfig.
+        final String extraInfo = (transport == TRANSPORT_CELLULAR) ? "internet.apn" : "";
+        mNetworkAgentConfig = new NetworkAgentConfig.Builder()
+                .setLegacyType(type)
+                .setLegacyTypeName(typeName)
+                .setLegacyExtraInfo(extraInfo)
+                .build();
+        mNetworkAgent = makeNetworkAgent(linkProperties, mNetworkAgentConfig);
     }
 
     protected InstrumentedNetworkAgent makeNetworkAgent(LinkProperties linkProperties,
-            final int type, final String typeName)
-            throws Exception {
-        return new InstrumentedNetworkAgent(this, linkProperties, type, typeName);
+            final NetworkAgentConfig nac) throws Exception {
+        return new InstrumentedNetworkAgent(this, linkProperties, nac);
     }
 
     public static class InstrumentedNetworkAgent extends NetworkAgent {
@@ -129,11 +136,9 @@
         private static final String PROVIDER_NAME = "InstrumentedNetworkAgentProvider";
 
         public InstrumentedNetworkAgent(NetworkAgentWrapper wrapper, LinkProperties lp,
-                final int type, final String typeName) {
+                NetworkAgentConfig nac) {
             super(wrapper.mContext, wrapper.mHandlerThread.getLooper(), wrapper.mLogTag,
-                    wrapper.mNetworkCapabilities, lp, wrapper.mScore,
-                    new NetworkAgentConfig.Builder()
-                            .setLegacyType(type).setLegacyTypeName(typeName).build(),
+                    wrapper.mNetworkCapabilities, lp, wrapper.mScore, nac,
                     new NetworkProvider(wrapper.mContext, wrapper.mHandlerThread.getLooper(),
                             PROVIDER_NAME));
             mWrapper = wrapper;
@@ -301,6 +306,14 @@
         return mNetworkCapabilities;
     }
 
+    public int getLegacyType() {
+        return mNetworkAgentConfig.getLegacyType();
+    }
+
+    public String getExtraInfo() {
+        return mNetworkAgentConfig.getLegacyExtraInfo();
+    }
+
     public @NonNull ArrayTrackRecord<CallbackType>.ReadHead getCallbackHistory() {
         return mCallbackHistory;
     }
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index 6a09b02..6fc605e 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -220,7 +220,7 @@
 
         // register callback
         when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(),
-                any(), nullable(String.class))).thenReturn(request);
+                anyInt(), any(), nullable(String.class))).thenReturn(request);
         manager.requestNetwork(request, callback, handler);
 
         // callback triggers
@@ -248,7 +248,7 @@
 
         // register callback
         when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(),
-                any(), nullable(String.class))).thenReturn(req1);
+                anyInt(), any(), nullable(String.class))).thenReturn(req1);
         manager.requestNetwork(req1, callback, handler);
 
         // callback triggers
@@ -266,7 +266,7 @@
 
         // callback can be registered again
         when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(),
-                any(), nullable(String.class))).thenReturn(req2);
+                anyInt(), any(), nullable(String.class))).thenReturn(req2);
         manager.requestNetwork(req2, callback, handler);
 
         // callback triggers
@@ -289,8 +289,8 @@
         info.targetSdkVersion = VERSION_CODES.N_MR1 + 1;
 
         when(mCtx.getApplicationInfo()).thenReturn(info);
-        when(mService.requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(), any(),
-                nullable(String.class))).thenReturn(request);
+        when(mService.requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(), anyInt(),
+                any(), nullable(String.class))).thenReturn(request);
 
         Handler handler = new Handler(Looper.getMainLooper());
         manager.requestNetwork(request, callback, handler);
@@ -358,34 +358,34 @@
 
         manager.requestNetwork(request, callback);
         verify(mService).requestNetwork(eq(request.networkCapabilities),
-                eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
+                eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
                 eq(testPkgName), eq(testAttributionTag));
         reset(mService);
 
         // Verify that register network callback does not calls requestNetwork at all.
         manager.registerNetworkCallback(request, callback);
-        verify(mService, never()).requestNetwork(any(), anyInt(), any(), anyInt(), any(),
+        verify(mService, never()).requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(),
                 anyInt(), any(), any());
-        verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(),
+        verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(), anyInt(),
                 eq(testPkgName), eq(testAttributionTag));
         reset(mService);
 
         manager.registerDefaultNetworkCallback(callback);
         verify(mService).requestNetwork(eq(null),
-                eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
-                eq(testPkgName), eq(testAttributionTag));
-        reset(mService);
-
-        manager.requestBackgroundNetwork(request, null, callback);
-        verify(mService).requestNetwork(eq(request.networkCapabilities),
-                eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
+                eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
                 eq(testPkgName), eq(testAttributionTag));
         reset(mService);
 
         Handler handler = new Handler(ConnectivityThread.getInstanceLooper());
+        manager.requestBackgroundNetwork(request, handler, callback);
+        verify(mService).requestNetwork(eq(request.networkCapabilities),
+                eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
+                eq(testPkgName), eq(testAttributionTag));
+        reset(mService);
+
         manager.registerSystemDefaultNetworkCallback(callback, handler);
         verify(mService).requestNetwork(eq(null),
-                eq(TRACK_SYSTEM_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
+                eq(TRACK_SYSTEM_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
                 eq(testPkgName), eq(testAttributionTag));
         reset(mService);
     }
diff --git a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt b/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt
index 9b0cfa9..c1315f6 100644
--- a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt
+++ b/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt
@@ -21,7 +21,7 @@
 import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER
 import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE
 import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY
-import android.net.util.MultinetworkPolicyTracker.ActiveDataSubscriptionIdChangedListener
+import android.net.util.MultinetworkPolicyTracker.ActiveDataSubscriptionIdListener
 import android.provider.Settings
 import android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI
 import android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE
@@ -120,9 +120,9 @@
                 MULTIPATH_PREFERENCE_PERFORMANCE.toString())
 
         val listenerCaptor = ArgumentCaptor.forClass(
-                ActiveDataSubscriptionIdChangedListener::class.java)
+                ActiveDataSubscriptionIdListener::class.java)
         verify(telephonyManager, times(1))
-                .registerPhoneStateListener(any(), listenerCaptor.capture())
+                .registerTelephonyCallback(any(), listenerCaptor.capture())
         val listener = listenerCaptor.value
         listener.onActiveDataSubscriptionIdChanged(testSubId)
 
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 2546580..b894b02 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -72,6 +72,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
@@ -201,6 +202,7 @@
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkRequest;
+import android.net.NetworkScore;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
 import android.net.NetworkStackClient;
@@ -265,7 +267,6 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.app.IBatteryStats;
 import com.android.internal.net.VpnConfig;
 import com.android.internal.net.VpnProfile;
 import com.android.internal.util.ArrayUtils;
@@ -275,6 +276,7 @@
 import com.android.net.module.util.ArrayTrackRecord;
 import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
 import com.android.server.connectivity.ConnectivityConstants;
+import com.android.server.connectivity.ConnectivityResources;
 import com.android.server.connectivity.MockableSystemProperties;
 import com.android.server.connectivity.Nat464Xlat;
 import com.android.server.connectivity.NetworkAgentInfo;
@@ -427,7 +429,6 @@
     @Mock DeviceIdleInternal mDeviceIdleInternal;
     @Mock INetworkManagementService mNetworkManagementService;
     @Mock NetworkStatsManager mStatsManager;
-    @Mock IBatteryStats mBatteryStatsService;
     @Mock IDnsResolver mMockDnsResolver;
     @Mock INetd mMockNetd;
     @Mock NetworkStackClient mNetworkStack;
@@ -445,6 +446,7 @@
     @Mock NetworkPolicyManager mNetworkPolicyManager;
     @Mock VpnProfileStore mVpnProfileStore;
     @Mock SystemConfigManager mSystemConfigManager;
+    @Mock Resources mResources;
 
     private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
             ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -472,7 +474,7 @@
     private class MockContext extends BroadcastInterceptingContext {
         private final MockContentResolver mContentResolver;
 
-        @Spy private Resources mResources;
+        @Spy private Resources mInternalResources;
         private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>();
 
         // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant
@@ -481,21 +483,15 @@
         MockContext(Context base, ContentProvider settingsProvider) {
             super(base);
 
-            mResources = spy(base.getResources());
-            when(mResources.getStringArray(com.android.internal.R.array.networkAttributes)).
-                    thenReturn(new String[] {
+            mInternalResources = spy(base.getResources());
+            when(mInternalResources.getStringArray(com.android.internal.R.array.networkAttributes))
+                    .thenReturn(new String[] {
                             "wifi,1,1,1,-1,true",
                             "mobile,0,0,0,-1,true",
                             "mobile_mms,2,0,2,60000,true",
                             "mobile_supl,3,0,2,60000,true",
                     });
 
-            when(mResources.getStringArray(
-                    com.android.internal.R.array.config_wakeonlan_supported_interfaces))
-                    .thenReturn(new String[]{
-                            WIFI_WOL_IFNAME,
-                    });
-
             mContentResolver = new MockContentResolver();
             mContentResolver.addProvider(Settings.AUTHORITY, settingsProvider);
         }
@@ -560,7 +556,7 @@
 
         @Override
         public Resources getResources() {
-            return mResources;
+            return mInternalResources;
         }
 
         @Override
@@ -719,7 +715,7 @@
 
         @Override
         protected InstrumentedNetworkAgent makeNetworkAgent(LinkProperties linkProperties,
-                final int type, final String typeName) throws Exception {
+                NetworkAgentConfig nac) throws Exception {
             mNetworkMonitor = mock(INetworkMonitor.class);
 
             final Answer validateAnswer = inv -> {
@@ -738,8 +734,8 @@
                     any() /* name */,
                     nmCbCaptor.capture());
 
-            final InstrumentedNetworkAgent na = new InstrumentedNetworkAgent(this, linkProperties,
-                    type, typeName) {
+            final InstrumentedNetworkAgent na =
+                    new InstrumentedNetworkAgent(this, linkProperties, nac) {
                 @Override
                 public void networkStatus(int status, String redirectUrl) {
                     mRedirectUrl = redirectUrl;
@@ -1083,6 +1079,10 @@
         public void triggerUnfulfillable(NetworkRequest r) {
             super.releaseRequestAsUnfulfillableByAnyFactory(r);
         }
+
+        public void assertNoRequestChanged() {
+            assertNull(mRequestHistory.poll(0, r -> true));
+        }
     }
 
     private Set<UidRange> uidRangesForUids(int... uids) {
@@ -1455,6 +1455,8 @@
         applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
         when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
                 .thenReturn(applicationInfo);
+        when(mPackageManager.getTargetSdkVersion(anyString()))
+                .thenReturn(applicationInfo.targetSdkVersion);
         when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]);
 
         // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
@@ -1526,12 +1528,23 @@
         doReturn(mSystemProperties).when(deps).getSystemProperties();
         doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
         doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt());
-        doReturn(mBatteryStatsService).when(deps).getBatteryStatsService();
         doAnswer(inv -> {
             mPolicyTracker = new WrappedMultinetworkPolicyTracker(
                     inv.getArgument(0), inv.getArgument(1), inv.getArgument(2));
             return mPolicyTracker;
         }).when(deps).makeMultinetworkPolicyTracker(any(), any(), any());
+        doReturn(true).when(deps).getCellular464XlatEnabled();
+
+        doReturn(60000).when(mResources).getInteger(
+                com.android.connectivity.resources.R.integer.config_networkTransitionTimeout);
+        doReturn("").when(mResources).getString(
+                com.android.connectivity.resources.R.string.config_networkCaptivePortalServerUrl);
+        doReturn(new String[]{ WIFI_WOL_IFNAME }).when(mResources).getStringArray(
+                com.android.connectivity.resources.R.array.config_wakeonlan_supported_interfaces);
+        final com.android.server.connectivity.ConnectivityResources connRes = mock(
+                ConnectivityResources.class);
+        doReturn(mResources).when(connRes).get();
+        doReturn(connRes).when(deps).getResources(any());
 
         return deps;
     }
@@ -1742,11 +1755,29 @@
         return expected;
     }
 
+    private boolean extraInfoInBroadcastHasExpectedNullness(NetworkInfo ni) {
+        final DetailedState state = ni.getDetailedState();
+        if (state == DetailedState.CONNECTED && ni.getExtraInfo() == null) return false;
+        // Expect a null extraInfo if the network is CONNECTING, because a CONNECTIVITY_ACTION
+        // broadcast with a state of CONNECTING only happens due to legacy VPN lockdown, which also
+        // nulls out extraInfo.
+        if (state == DetailedState.CONNECTING && ni.getExtraInfo() != null) return false;
+        // Can't make any assertions about DISCONNECTED broadcasts. When a network actually
+        // disconnects, disconnectAndDestroyNetwork sets its state to DISCONNECTED and its extraInfo
+        // to null. But if the DISCONNECTED broadcast is just simulated by LegacyTypeTracker due to
+        // a network switch, extraInfo will likely be populated.
+        // This is likely a bug in CS, but likely not one we can fix without impacting apps.
+        return true;
+    }
+
     private ExpectedBroadcast expectConnectivityAction(int type, NetworkInfo.DetailedState state) {
-        return registerConnectivityBroadcastThat(1, intent ->
-                type == intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) && state.equals(
-                        ((NetworkInfo) intent.getParcelableExtra(EXTRA_NETWORK_INFO))
-                                .getDetailedState()));
+        return registerConnectivityBroadcastThat(1, intent -> {
+            final int actualType = intent.getIntExtra(EXTRA_NETWORK_TYPE, -1);
+            final NetworkInfo ni = intent.getParcelableExtra(EXTRA_NETWORK_INFO);
+            return type == actualType
+                    && state == ni.getDetailedState()
+                    && extraInfoInBroadcastHasExpectedNullness(ni);
+        });
     }
 
     @Test
@@ -3732,8 +3763,8 @@
             networkCapabilities.addTransportType(TRANSPORT_WIFI)
                     .setNetworkSpecifier(new MatchAllNetworkSpecifier());
             mService.requestNetwork(networkCapabilities, NetworkRequest.Type.REQUEST.ordinal(),
-                    null, 0, null, ConnectivityManager.TYPE_WIFI, mContext.getPackageName(),
-                    getAttributionTag());
+                    null, 0, null, ConnectivityManager.TYPE_WIFI, NetworkCallback.FLAG_NONE,
+                    mContext.getPackageName(), getAttributionTag());
         });
 
         class NonParcelableSpecifier extends NetworkSpecifier {
@@ -4010,7 +4041,8 @@
         grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid());
         final TestNetworkCallback cellBgCallback = new TestNetworkCallback();
         mCm.requestBackgroundNetwork(new NetworkRequest.Builder()
-                .addTransportType(TRANSPORT_CELLULAR).build(), null, cellBgCallback);
+                .addTransportType(TRANSPORT_CELLULAR).build(),
+                mCsHandlerThread.getThreadHandler(), cellBgCallback);
 
         // Make callbacks for monitoring.
         final NetworkRequest request = new NetworkRequest.Builder().build();
@@ -5574,7 +5606,7 @@
         reset(mStatsManager);
 
         // Temp metered change shouldn't update ifaces
-        mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+        mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
         waitForIdle();
         verify(mStatsManager, never()).notifyNetworkStatus(eq(Arrays.asList(onlyCell)),
                 any(List.class), eq(MOBILE_IFNAME), any(List.class));
@@ -7184,12 +7216,14 @@
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mCellNetworkAgent);
 
         setUidRulesChanged(RULE_REJECT_ALL);
         cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
         assertNull(mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+        assertExtraInfoFromCmBlocked(mCellNetworkAgent);
 
         // ConnectivityService should cache it not to invoke the callback again.
         setUidRulesChanged(RULE_REJECT_METERED);
@@ -7200,12 +7234,14 @@
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mCellNetworkAgent);
 
         setUidRulesChanged(RULE_REJECT_METERED);
         cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
         assertNull(mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+        assertExtraInfoFromCmBlocked(mCellNetworkAgent);
 
         // Restrict the network based on UID rule and NOT_METERED capability change.
         mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
@@ -7214,6 +7250,7 @@
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mCellNetworkAgent);
 
         mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
         cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED,
@@ -7222,12 +7259,14 @@
         assertNull(mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+        assertExtraInfoFromCmBlocked(mCellNetworkAgent);
 
         setUidRulesChanged(RULE_ALLOW_METERED);
         cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mCellNetworkAgent);
 
         setUidRulesChanged(RULE_NONE);
         cellNetworkCallback.assertNoCallback();
@@ -7238,6 +7277,7 @@
         assertNull(mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+        assertExtraInfoFromCmBlocked(mCellNetworkAgent);
         setRestrictBackgroundChanged(true);
         cellNetworkCallback.assertNoCallback();
 
@@ -7245,12 +7285,14 @@
         cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mCellNetworkAgent);
 
         setRestrictBackgroundChanged(false);
         cellNetworkCallback.assertNoCallback();
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mCellNetworkAgent);
 
         mCm.unregisterNetworkCallback(cellNetworkCallback);
     }
@@ -7309,6 +7351,15 @@
         assertNotNull(ni);
         assertEquals(type, ni.getType());
         assertEquals(ConnectivityManager.getNetworkTypeName(type), state, ni.getDetailedState());
+        if (state == DetailedState.CONNECTED || state == DetailedState.SUSPENDED) {
+            assertNotNull(ni.getExtraInfo());
+        } else {
+            // Technically speaking, a network that's in CONNECTING state will generally have a
+            // non-null extraInfo. This doesn't actually happen in this test because it never calls
+            // a legacy API while a network is connecting. When a network is in CONNECTING state
+            // because of legacy lockdown VPN, its extraInfo is always null.
+            assertNull(ni.getExtraInfo());
+        }
     }
 
     private void assertActiveNetworkInfo(int type, DetailedState state) {
@@ -7318,6 +7369,26 @@
         checkNetworkInfo(mCm.getNetworkInfo(type), type, state);
     }
 
+    private void assertExtraInfoFromCm(TestNetworkAgentWrapper network, boolean present) {
+        final NetworkInfo niForNetwork = mCm.getNetworkInfo(network.getNetwork());
+        final NetworkInfo niForType = mCm.getNetworkInfo(network.getLegacyType());
+        if (present) {
+            assertEquals(network.getExtraInfo(), niForNetwork.getExtraInfo());
+            assertEquals(network.getExtraInfo(), niForType.getExtraInfo());
+        } else {
+            assertNull(niForNetwork.getExtraInfo());
+            assertNull(niForType.getExtraInfo());
+        }
+    }
+
+    private void assertExtraInfoFromCmBlocked(TestNetworkAgentWrapper network) {
+        assertExtraInfoFromCm(network, false);
+    }
+
+    private void assertExtraInfoFromCmPresent(TestNetworkAgentWrapper network) {
+        assertExtraInfoFromCm(network, true);
+    }
+
     // Checks that each of the |agents| receive a blocked status change callback with the specified
     // |blocked| value, in any order. This is needed because when an event affects multiple
     // networks, ConnectivityService does not guarantee the order in which callbacks are fired.
@@ -7632,6 +7703,7 @@
         assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED);
+        assertExtraInfoFromCmBlocked(mCellNetworkAgent);
 
         // TODO: it would be nice if we could simply rely on the production code here, and have
         // LockdownVpnTracker start the VPN, have the VPN code register its NetworkAgent with
@@ -7660,6 +7732,7 @@
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
         assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mCellNetworkAgent);
         assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
         assertTrue(vpnNc.hasTransport(TRANSPORT_CELLULAR));
         assertFalse(vpnNc.hasTransport(TRANSPORT_WIFI));
@@ -7702,6 +7775,7 @@
         assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED);
+        assertExtraInfoFromCmBlocked(mWiFiNetworkAgent);
 
         // The VPN comes up again on wifi.
         b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
@@ -7716,6 +7790,7 @@
         assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mWiFiNetworkAgent);
         vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
         assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
         assertTrue(vpnNc.hasTransport(TRANSPORT_WIFI));
@@ -7732,6 +7807,7 @@
         assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mWiFiNetworkAgent);
 
         b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED);
         mWiFiNetworkAgent.disconnect();
@@ -7808,18 +7884,18 @@
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
         mCellNetworkAgent.connect(true);
         waitForIdle();
-        verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(),
+        verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext,
+                cellLp.getInterfaceName(),
                 new int[] { TRANSPORT_CELLULAR });
-        reset(mBatteryStatsService);
 
         final LinkProperties wifiLp = new LinkProperties();
         wifiLp.setInterfaceName("wifi0");
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
         mWiFiNetworkAgent.connect(true);
         waitForIdle();
-        verify(mBatteryStatsService).noteNetworkInterfaceForTransports(wifiLp.getInterfaceName(),
+        verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext,
+                wifiLp.getInterfaceName(),
                 new int[] { TRANSPORT_WIFI });
-        reset(mBatteryStatsService);
 
         mCellNetworkAgent.disconnect();
         mWiFiNetworkAgent.disconnect();
@@ -7828,7 +7904,8 @@
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
         mCellNetworkAgent.connect(true);
         waitForIdle();
-        verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(),
+        verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext,
+                cellLp.getInterfaceName(),
                 new int[] { TRANSPORT_CELLULAR });
         mCellNetworkAgent.disconnect();
     }
@@ -7901,7 +7978,6 @@
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
         reset(mMockDnsResolver);
         reset(mMockNetd);
-        reset(mBatteryStatsService);
 
         // Connect with ipv6 link properties. Expect prefix discovery to be started.
         mCellNetworkAgent.connect(true);
@@ -7912,7 +7988,8 @@
         assertRoutesAdded(cellNetId, ipv6Subnet, defaultRoute);
         verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId));
         verify(mMockNetd, times(1)).networkAddInterface(cellNetId, MOBILE_IFNAME);
-        verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(),
+        verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext,
+                cellLp.getInterfaceName(),
                 new int[] { TRANSPORT_CELLULAR });
 
         networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
@@ -7933,8 +8010,8 @@
         // Make sure BatteryStats was not told about any v4- interfaces, as none should have
         // come online yet.
         waitForIdle();
-        verify(mBatteryStatsService, never()).noteNetworkInterfaceForTransports(startsWith("v4-"),
-                any());
+        verify(mDeps, never())
+                .reportNetworkInterfaceForTransports(eq(mServiceContext), startsWith("v4-"), any());
 
         verifyNoMoreInteractions(mMockNetd);
         verifyNoMoreInteractions(mMockDnsResolver);
@@ -7986,8 +8063,9 @@
         assertTrue(ArrayUtils.contains(resolvrParams.servers, "8.8.8.8"));
 
         for (final LinkProperties stackedLp : stackedLpsAfterChange) {
-            verify(mBatteryStatsService).noteNetworkInterfaceForTransports(
-                    stackedLp.getInterfaceName(), new int[] { TRANSPORT_CELLULAR });
+            verify(mDeps).reportNetworkInterfaceForTransports(
+                    mServiceContext, stackedLp.getInterfaceName(),
+                    new int[] { TRANSPORT_CELLULAR });
         }
         reset(mMockNetd);
         when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME))
@@ -8262,6 +8340,45 @@
     }
 
     @Test
+    public void testWith464XlatDisable() throws Exception {
+        doReturn(false).when(mDeps).getCellular464XlatEnabled();
+
+        final TestNetworkCallback callback = new TestNetworkCallback();
+        final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+        final NetworkRequest networkRequest = new NetworkRequest.Builder()
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .build();
+        mCm.registerNetworkCallback(networkRequest, callback);
+        mCm.registerDefaultNetworkCallback(defaultCallback);
+
+        // Bring up validated cell.
+        final LinkProperties cellLp = new LinkProperties();
+        cellLp.setInterfaceName(MOBILE_IFNAME);
+        cellLp.addLinkAddress(new LinkAddress("2001:db8:1::1/64"));
+        cellLp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, MOBILE_IFNAME));
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+
+        mCellNetworkAgent.sendLinkProperties(cellLp);
+        mCellNetworkAgent.connect(true);
+        callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        final int cellNetId = mCellNetworkAgent.getNetwork().netId;
+        waitForIdle();
+
+        verify(mMockDnsResolver, never()).startPrefix64Discovery(cellNetId);
+        Nat464Xlat clat = getNat464Xlat(mCellNetworkAgent);
+        assertTrue("Nat464Xlat was not IDLE", !clat.isStarted());
+
+        // This cannot happen because prefix discovery cannot succeed if it is never started.
+        mService.mResolverUnsolEventCallback.onNat64PrefixEvent(
+                makeNat64PrefixEvent(cellNetId, PREFIX_OPERATION_ADDED, "64:ff9b::", 96));
+
+        // ... but still, check that even if it did, clatd would not be started.
+        verify(mMockNetd, never()).clatdStart(anyString(), anyString());
+        assertTrue("Nat464Xlat was not IDLE", !clat.isStarted());
+    }
+
+    @Test
     public void testDataActivityTracking() throws Exception {
         final TestNetworkCallback networkCallback = new TestNetworkCallback();
         final NetworkRequest networkRequest = new NetworkRequest.Builder()
@@ -8653,6 +8770,7 @@
         applicationInfo.targetSdkVersion = targetSdk;
         when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
                 .thenReturn(applicationInfo);
+        when(mPackageManager.getTargetSdkVersion(any())).thenReturn(targetSdk);
 
         when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
 
@@ -8667,102 +8785,183 @@
         }
     }
 
-    private int getOwnerUidNetCapsForCallerPermission(int ownerUid, int callerUid) {
+    private int getOwnerUidNetCapsPermission(int ownerUid, int callerUid,
+            boolean includeLocationSensitiveInfo) {
         final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid);
 
         return mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                netCap, callerUid, mContext.getPackageName(), getAttributionTag()).getOwnerUid();
+                netCap, includeLocationSensitiveInfo, callerUid,
+                mContext.getPackageName(), getAttributionTag())
+                .getOwnerUid();
     }
 
-    private void verifyWifiInfoCopyNetCapsForCallerPermission(
-            int callerUid, boolean shouldMakeCopyWithLocationSensitiveFieldsParcelable) {
+    private void verifyWifiInfoCopyNetCapsPermission(
+            int callerUid, boolean includeLocationSensitiveInfo,
+            boolean shouldMakeCopyWithLocationSensitiveFieldsParcelable) {
         final WifiInfo wifiInfo = mock(WifiInfo.class);
         when(wifiInfo.hasLocationSensitiveFields()).thenReturn(true);
         final NetworkCapabilities netCap = new NetworkCapabilities().setTransportInfo(wifiInfo);
 
         mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                netCap, callerUid, mContext.getPackageName(), getAttributionTag());
+                netCap, includeLocationSensitiveInfo, callerUid,
+                mContext.getPackageName(), getAttributionTag());
         verify(wifiInfo).makeCopy(eq(shouldMakeCopyWithLocationSensitiveFieldsParcelable));
     }
 
+    private void verifyOwnerUidAndWifiInfoNetCapsPermission(
+            boolean shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag,
+            boolean shouldInclLocationSensitiveOwnerUidWithIncludeFlag,
+            boolean shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag,
+            boolean shouldInclLocationSensitiveWifiInfoWithIncludeFlag) {
+        final int myUid = Process.myUid();
+
+        final int expectedOwnerUidWithoutIncludeFlag =
+                shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag
+                        ? Process.myUid() : INVALID_UID;
+        assertEquals(expectedOwnerUidWithoutIncludeFlag, getOwnerUidNetCapsPermission(
+                myUid, myUid, false /* includeLocationSensitiveInfo */));
+
+        final int expectedOwnerUidWithIncludeFlag =
+                shouldInclLocationSensitiveOwnerUidWithIncludeFlag ? myUid : INVALID_UID;
+        assertEquals(expectedOwnerUidWithIncludeFlag, getOwnerUidNetCapsPermission(
+                myUid, myUid, true /* includeLocationSensitiveInfo */));
+
+        verifyWifiInfoCopyNetCapsPermission(myUid,
+                false, /* includeLocationSensitiveInfo */
+                shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag);
+
+        verifyWifiInfoCopyNetCapsPermission(myUid,
+                true, /* includeLocationSensitiveInfo */
+                shouldInclLocationSensitiveWifiInfoWithIncludeFlag);
+
+    }
+
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedWithFineLocationAfterQ()
+    public void testCreateWithLocationInfoSanitizedWithFineLocationAfterQ()
             throws Exception {
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
-        final int myUid = Process.myUid();
-        assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        verifyOwnerUidAndWifiInfoNetCapsPermission(
+                // Ensure that we include owner uid even if the request asks to remove it since the
+                // app has necessary permissions and targetSdk < S.
+                true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+                // Ensure that we remove location info if the request asks to remove it even if the
+                // app has necessary permissions.
+                true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+        );
     }
 
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedWithCoarseLocationPreQ()
+    public void testCreateWithLocationInfoSanitizedWithFineLocationPreSWithAndWithoutCallbackFlag()
+            throws Exception {
+        setupLocationPermissions(Build.VERSION_CODES.R, true, AppOpsManager.OPSTR_FINE_LOCATION,
+                Manifest.permission.ACCESS_FINE_LOCATION);
+
+        verifyOwnerUidAndWifiInfoNetCapsPermission(
+                // Ensure that we include owner uid even if the request asks to remove it since the
+                // app has necessary permissions and targetSdk < S.
+                true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+                // Ensure that we remove location info if the request asks to remove it even if the
+                // app has necessary permissions.
+                true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+        );
+    }
+
+    @Test
+    public void
+            testCreateWithLocationInfoSanitizedWithFineLocationAfterSWithAndWithoutCallbackFlag()
+            throws Exception {
+        setupLocationPermissions(Build.VERSION_CODES.S, true, AppOpsManager.OPSTR_FINE_LOCATION,
+                Manifest.permission.ACCESS_FINE_LOCATION);
+
+        verifyOwnerUidAndWifiInfoNetCapsPermission(
+                // Ensure that we owner UID if the request asks us to remove it even if the app
+                // has necessary permissions since targetSdk >= S.
+                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+                // Ensure that we remove location info if the request asks to remove it even if the
+                // app has necessary permissions.
+                true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+        );
+    }
+
+    @Test
+    public void testCreateWithLocationInfoSanitizedWithCoarseLocationPreQ()
             throws Exception {
         setupLocationPermissions(Build.VERSION_CODES.P, true, AppOpsManager.OPSTR_COARSE_LOCATION,
                 Manifest.permission.ACCESS_COARSE_LOCATION);
 
-        final int myUid = Process.myUid();
-        assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        verifyOwnerUidAndWifiInfoNetCapsPermission(
+                // Ensure that we owner UID if the request asks us to remove it even if the app
+                // has necessary permissions since targetSdk >= S.
+                true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+                // Ensure that we remove location info if the request asks to remove it even if the
+                // app has necessary permissions.
+                true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+        );
     }
 
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedLocationOff() throws Exception {
+    public void testCreateWithLocationInfoSanitizedLocationOff() throws Exception {
         // Test that even with fine location permission, and UIDs matching, the UID is sanitized.
         setupLocationPermissions(Build.VERSION_CODES.Q, false, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
-        final int myUid = Process.myUid();
-        assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        verifyOwnerUidAndWifiInfoNetCapsPermission(
+                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+                false /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+        );
     }
 
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedWrongUid() throws Exception {
+    public void testCreateWithLocationInfoSanitizedWrongUid() throws Exception {
         // Test that even with fine location permission, not being the owner leads to sanitization.
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
         final int myUid = Process.myUid();
-        assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid + 1, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        assertEquals(Process.INVALID_UID,
+                getOwnerUidNetCapsPermission(myUid + 1, myUid,
+                        true /* includeLocationSensitiveInfo */));
     }
 
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedWithCoarseLocationAfterQ()
+    public void testCreateWithLocationInfoSanitizedWithCoarseLocationAfterQ()
             throws Exception {
         // Test that not having fine location permission leads to sanitization.
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_COARSE_LOCATION,
                 Manifest.permission.ACCESS_COARSE_LOCATION);
 
-        // Test that without the location permission, the owner field is sanitized.
-        final int myUid = Process.myUid();
-        assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        verifyOwnerUidAndWifiInfoNetCapsPermission(
+                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+                false /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+        );
     }
 
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedWithoutLocationPermission()
+    public void testCreateWithLocationInfoSanitizedWithoutLocationPermission()
             throws Exception {
+        // Test that not having fine location permission leads to sanitization.
         setupLocationPermissions(Build.VERSION_CODES.Q, true, null /* op */, null /* perm */);
 
-        // Test that without the location permission, the owner field is sanitized.
-        final int myUid = Process.myUid();
-        assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        verifyOwnerUidAndWifiInfoNetCapsPermission(
+                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+                false /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+        );
     }
 
     private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
@@ -8966,8 +9165,9 @@
                 ConnectivityManager.getNetworkTypeName(TYPE_MOBILE),
                 TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_LTE));
         return new NetworkAgentInfo(null, new Network(NET_ID), info, new LinkProperties(),
-                nc, 0, mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0,
-                INVALID_UID, mQosCallbackTracker);
+                nc, new NetworkScore.Builder().setLegacyInt(0).build(),
+                mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0,
+                INVALID_UID, mQosCallbackTracker, new ConnectivityService.Dependencies());
     }
 
     @Test
@@ -9353,8 +9553,8 @@
             assertThrows("Expect throws for invalid request type " + reqTypeInt,
                     IllegalArgumentException.class,
                     () -> mService.requestNetwork(nc, reqTypeInt, null, 0, null,
-                            ConnectivityManager.TYPE_NONE, mContext.getPackageName(),
-                            getAttributionTag())
+                            ConnectivityManager.TYPE_NONE, NetworkCallback.FLAG_NONE,
+                            mContext.getPackageName(), getAttributionTag())
             );
         }
     }
@@ -10647,7 +10847,7 @@
                 null,
                 null);
 
-        // default NCs will be unregistered in tearDown
+        // default callbacks will be unregistered in tearDown
     }
 
     /**
@@ -10704,7 +10904,7 @@
                 null,
                 mService.mNoServiceNetwork.network());
 
-        // default NCs will be unregistered in tearDown
+        // default callbacks will be unregistered in tearDown
     }
 
     /**
@@ -10763,7 +10963,7 @@
                 null,
                 mService.mNoServiceNetwork.network());
 
-        // default NCs will be unregistered in tearDown
+        // default callbacks will be unregistered in tearDown
     }
 
     /**
@@ -10822,7 +11022,28 @@
                 null,
                 mService.mNoServiceNetwork.network());
 
-        // default NCs will be unregistered in tearDown
+        // default callbacks will be unregistered in tearDown
+    }
+
+    @Test
+    public void testCapabilityWithOemNetworkPreference() throws Exception {
+        @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+                OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY;
+        setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(networkPref);
+        registerDefaultNetworkCallbacks();
+
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
+
+        mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+
+        mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+        mSystemDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc ->
+                nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+        mDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc ->
+                nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+
+        // default callbacks will be unregistered in tearDown
     }
 
     @Test
@@ -10903,4 +11124,100 @@
         verifyNoNetwork();
         mCm.unregisterNetworkCallback(cellCb);
     }
+
+    // Cannot be part of MockNetworkFactory since it requires method of the test.
+    private void expectNoRequestChanged(@NonNull MockNetworkFactory factory) {
+        waitForIdle();
+        factory.assertNoRequestChanged();
+    }
+
+    @Test
+    public void testRegisterBestMatchingNetworkCallback_noIssueToFactory() throws Exception {
+        // Prepare mock mms factory.
+        final HandlerThread handlerThread = new HandlerThread("MockCellularFactory");
+        handlerThread.start();
+        NetworkCapabilities filter = new NetworkCapabilities()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NET_CAPABILITY_MMS);
+        final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
+                mServiceContext, "testFactory", filter, mCsHandlerThread);
+        testFactory.setScoreFilter(40);
+
+        try {
+            // Register the factory and expect it will see default request, because all requests
+            // are sent to all factories.
+            testFactory.register();
+            testFactory.expectRequestAdd();
+            testFactory.assertRequestCountEquals(1);
+            // The factory won't try to start the network since the default request doesn't
+            // match the filter (no INTERNET capability).
+            assertFalse(testFactory.getMyStartRequested());
+
+            // Register callback for listening best matching network. Verify that the request won't
+            // be sent to factory.
+            final TestNetworkCallback bestMatchingCb = new TestNetworkCallback();
+            mCm.registerBestMatchingNetworkCallback(
+                    new NetworkRequest.Builder().addCapability(NET_CAPABILITY_MMS).build(),
+                    bestMatchingCb, mCsHandlerThread.getThreadHandler());
+            bestMatchingCb.assertNoCallback();
+            expectNoRequestChanged(testFactory);
+            testFactory.assertRequestCountEquals(1);
+            assertFalse(testFactory.getMyStartRequested());
+
+            // Fire a normal mms request, verify the factory will only see the request.
+            final TestNetworkCallback mmsNetworkCallback = new TestNetworkCallback();
+            final NetworkRequest mmsRequest = new NetworkRequest.Builder()
+                    .addCapability(NET_CAPABILITY_MMS).build();
+            mCm.requestNetwork(mmsRequest, mmsNetworkCallback);
+            testFactory.expectRequestAdd();
+            testFactory.assertRequestCountEquals(2);
+            assertTrue(testFactory.getMyStartRequested());
+
+            // Unregister best matching callback, verify factory see no change.
+            mCm.unregisterNetworkCallback(bestMatchingCb);
+            expectNoRequestChanged(testFactory);
+            testFactory.assertRequestCountEquals(2);
+            assertTrue(testFactory.getMyStartRequested());
+        } finally {
+            testFactory.terminate();
+        }
+    }
+
+    @Test
+    public void testRegisterBestMatchingNetworkCallback_trackBestNetwork() throws Exception {
+        final TestNetworkCallback bestMatchingCb = new TestNetworkCallback();
+        mCm.registerBestMatchingNetworkCallback(
+                new NetworkRequest.Builder().addCapability(NET_CAPABILITY_TRUSTED).build(),
+                bestMatchingCb, mCsHandlerThread.getThreadHandler());
+
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        bestMatchingCb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(true);
+        bestMatchingCb.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+
+        // Change something on cellular to trigger capabilities changed, since the callback
+        // only cares about the best network, verify it received nothing from cellular.
+        mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+        bestMatchingCb.assertNoCallback();
+
+        // Make cellular the best network again, verify the callback now tracks cellular.
+        mWiFiNetworkAgent.adjustScore(-50);
+        bestMatchingCb.expectAvailableCallbacksValidated(mCellNetworkAgent);
+
+        // Make cellular temporary non-trusted, which will not satisfying the request.
+        // Verify the callback switch from/to the other network accordingly.
+        mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
+        bestMatchingCb.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
+        mCellNetworkAgent.addCapability(NET_CAPABILITY_TRUSTED);
+        bestMatchingCb.expectAvailableDoubleValidatedCallbacks(mCellNetworkAgent);
+
+        // Verify the callback doesn't care about wifi disconnect.
+        mWiFiNetworkAgent.disconnect();
+        bestMatchingCb.assertNoCallback();
+        mCellNetworkAgent.disconnect();
+        bestMatchingCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+    }
 }
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index f97eabf..6232423 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -35,6 +35,7 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.net.ConnectivityManager;
 import android.net.INetd;
 import android.net.IpSecAlgorithm;
 import android.net.IpSecConfig;
@@ -47,6 +48,7 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.StructStat;
+import android.util.Range;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -647,9 +649,9 @@
 
     @Test
     public void testReserveNetId() {
-        int start = mIpSecService.TUN_INTF_NETID_START;
-        for (int i = 0; i < mIpSecService.TUN_INTF_NETID_RANGE; i++) {
-            assertEquals(start + i, mIpSecService.reserveNetId());
+        final Range<Integer> netIdRange = ConnectivityManager.getIpSecNetIdRange();
+        for (int netId = netIdRange.getLower(); netId <= netIdRange.getUpper(); netId++) {
+            assertEquals(netId, mIpSecService.reserveNetId());
         }
 
         // Check that resource exhaustion triggers an exception
@@ -661,7 +663,7 @@
 
         // Now release one and try again
         int releasedNetId =
-                mIpSecService.TUN_INTF_NETID_START + mIpSecService.TUN_INTF_NETID_RANGE / 2;
+                netIdRange.getLower() + (netIdRange.getUpper() - netIdRange.getLower()) / 2;
         mIpSecService.releaseNetId(releasedNetId);
         assertEquals(releasedNetId, mIpSecService.reserveNetId());
     }
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index a913673..ea2b362 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -40,6 +40,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkProvider;
+import android.net.NetworkScore;
 import android.os.Binder;
 import android.text.format.DateUtils;
 
@@ -355,9 +356,10 @@
         caps.addCapability(0);
         caps.addTransportType(transport);
         NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info,
-                new LinkProperties(), caps, 50, mCtx, null, new NetworkAgentConfig() /* config */,
-                mConnService, mNetd, mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(),
-                mQosCallbackTracker);
+                new LinkProperties(), caps, new NetworkScore.Builder().setLegacyInt(50).build(),
+                mCtx, null, new NetworkAgentConfig() /* config */, mConnService, mNetd,
+                mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(),
+                mQosCallbackTracker, new ConnectivityService.Dependencies());
         nai.everValidated = true;
         return nai;
     }
diff --git a/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java b/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
index 950d716..38f6d7f 100644
--- a/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
+++ b/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
@@ -46,12 +46,12 @@
 import android.content.pm.ApplicationInfo;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
+import android.net.EthernetNetworkSpecifier;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkTemplate;
-import android.net.StringNetworkSpecifier;
 import android.net.TelephonyNetworkSpecifier;
 import android.os.Handler;
 import android.os.UserHandle;
@@ -240,7 +240,7 @@
         NetworkCapabilities capabilities = new NetworkCapabilities()
                 .addCapability(NET_CAPABILITY_INTERNET)
                 .addTransportType(TRANSPORT_CELLULAR)
-                .setNetworkSpecifier(new StringNetworkSpecifier("234"));
+                .setNetworkSpecifier(new EthernetNetworkSpecifier("eth234"));
         if (!roaming) {
             capabilities.addCapability(NET_CAPABILITY_NOT_ROAMING);
         }
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
index 5f56e25..9b2a638 100644
--- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -16,11 +16,15 @@
 
 package com.android.server.connectivity;
 
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -34,6 +38,7 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkAgentConfig;
+import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.os.Handler;
 import android.os.test.TestLooper;
@@ -72,11 +77,15 @@
     Handler mHandler;
     NetworkAgentConfig mAgentConfig = new NetworkAgentConfig();
 
-    Nat464Xlat makeNat464Xlat() {
-        return new Nat464Xlat(mNai, mNetd, mDnsResolver) {
+    Nat464Xlat makeNat464Xlat(boolean isCellular464XlatEnabled) {
+        return new Nat464Xlat(mNai, mNetd, mDnsResolver, new ConnectivityService.Dependencies()) {
             @Override protected int getNetId() {
                 return NETID;
             }
+
+            @Override protected boolean isCellular464XlatEnabled() {
+                return isCellular464XlatEnabled;
+            }
         };
     }
 
@@ -99,6 +108,7 @@
         mNai.linkProperties.setInterfaceName(BASE_IFACE);
         mNai.networkInfo = new NetworkInfo(null);
         mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI);
+        mNai.networkCapabilities = new NetworkCapabilities();
         markNetworkConnected();
         when(mNai.connService()).thenReturn(mConnectivity);
         when(mNai.netAgentConfig()).thenReturn(mAgentConfig);
@@ -110,21 +120,23 @@
     }
 
     private void assertRequiresClat(boolean expected, NetworkAgentInfo nai) {
+        Nat464Xlat nat = makeNat464Xlat(true);
         String msg = String.format("requiresClat expected %b for type=%d state=%s skip=%b "
                 + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
                 nai.networkInfo.getDetailedState(),
                 mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
                 nai.linkProperties.getLinkAddresses());
-        assertEquals(msg, expected, Nat464Xlat.requiresClat(nai));
+        assertEquals(msg, expected, nat.requiresClat(nai));
     }
 
     private void assertShouldStartClat(boolean expected, NetworkAgentInfo nai) {
+        Nat464Xlat nat = makeNat464Xlat(true);
         String msg = String.format("shouldStartClat expected %b for type=%d state=%s skip=%b "
                 + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
                 nai.networkInfo.getDetailedState(),
                 mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
                 nai.linkProperties.getLinkAddresses());
-        assertEquals(msg, expected, Nat464Xlat.shouldStartClat(nai));
+        assertEquals(msg, expected, nat.shouldStartClat(nai));
     }
 
     @Test
@@ -194,7 +206,7 @@
     }
 
     private void checkNormalStartAndStop(boolean dueToDisconnect) throws Exception {
-        Nat464Xlat nat = makeNat464Xlat();
+        Nat464Xlat nat = makeNat464Xlat(true);
         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
 
         mNai.linkProperties.addLinkAddress(V6ADDR);
@@ -245,7 +257,7 @@
     }
 
     private void checkStartStopStart(boolean interfaceRemovedFirst) throws Exception {
-        Nat464Xlat nat = makeNat464Xlat();
+        Nat464Xlat nat = makeNat464Xlat(true);
         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
         InOrder inOrder = inOrder(mNetd, mConnectivity);
 
@@ -335,7 +347,7 @@
 
     @Test
     public void testClatdCrashWhileRunning() throws Exception {
-        Nat464Xlat nat = makeNat464Xlat();
+        Nat464Xlat nat = makeNat464Xlat(true);
         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
 
         nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
@@ -372,7 +384,7 @@
     }
 
     private void checkStopBeforeClatdStarts(boolean dueToDisconnect) throws Exception {
-        Nat464Xlat nat = makeNat464Xlat();
+        Nat464Xlat nat = makeNat464Xlat(true);
 
         mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
 
@@ -414,7 +426,7 @@
     }
 
     private void checkStopAndClatdNeverStarts(boolean dueToDisconnect) throws Exception {
-        Nat464Xlat nat = makeNat464Xlat();
+        Nat464Xlat nat = makeNat464Xlat(true);
 
         mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
 
@@ -450,7 +462,7 @@
         final IpPrefix prefixFromDns = new IpPrefix(NAT64_PREFIX);
         final IpPrefix prefixFromRa = new IpPrefix(OTHER_NAT64_PREFIX);
 
-        Nat464Xlat nat = makeNat464Xlat();
+        Nat464Xlat nat = makeNat464Xlat(true);
 
         final LinkProperties emptyLp = new LinkProperties();
         LinkProperties fixedupLp;
@@ -486,10 +498,57 @@
         assertEquals(null, fixedupLp.getNat64Prefix());
     }
 
+    private void checkClatDisabledOnCellular(boolean onCellular) throws Exception {
+        // Disable 464xlat on cellular networks.
+        Nat464Xlat nat = makeNat464Xlat(false);
+        mNai.linkProperties.addLinkAddress(V6ADDR);
+        mNai.networkCapabilities.setTransportType(TRANSPORT_CELLULAR, onCellular);
+        nat.update();
+
+        final IpPrefix nat64Prefix = new IpPrefix(NAT64_PREFIX);
+        if (onCellular) {
+            // Prefix discovery is never started.
+            verify(mDnsResolver, never()).startPrefix64Discovery(eq(NETID));
+            assertIdle(nat);
+
+            // If a NAT64 prefix comes in from an RA, clat is not started either.
+            mNai.linkProperties.setNat64Prefix(nat64Prefix);
+            nat.setNat64PrefixFromRa(nat64Prefix);
+            nat.update();
+            verify(mNetd, never()).clatdStart(anyString(), anyString());
+            assertIdle(nat);
+        } else {
+            // Prefix discovery is started.
+            verify(mDnsResolver).startPrefix64Discovery(eq(NETID));
+            assertIdle(nat);
+
+            // If a NAT64 prefix comes in from an RA, clat is started.
+            mNai.linkProperties.setNat64Prefix(nat64Prefix);
+            nat.setNat64PrefixFromRa(nat64Prefix);
+            nat.update();
+            verify(mNetd).clatdStart(BASE_IFACE, NAT64_PREFIX);
+            assertStarting(nat);
+        }
+    }
+
+    @Test
+    public void testClatDisabledOnCellular() throws Exception {
+        checkClatDisabledOnCellular(true);
+    }
+
+    @Test
+    public void testClatDisabledOnNonCellular() throws Exception {
+        checkClatDisabledOnCellular(false);
+    }
+
     static void assertIdle(Nat464Xlat nat) {
         assertTrue("Nat464Xlat was not IDLE", !nat.isStarted());
     }
 
+    static void assertStarting(Nat464Xlat nat) {
+        assertTrue("Nat464Xlat was not STARTING", nat.isStarting());
+    }
+
     static void assertRunning(Nat464Xlat nat) {
         assertTrue("Nat464Xlat was not RUNNING", nat.isRunning());
     }
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index e4e24b4..fec5ef3 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -48,18 +48,22 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.net.INetd;
 import android.net.UidRange;
+import android.net.Uri;
 import android.os.Build;
 import android.os.SystemConfigManager;
 import android.os.UserHandle;
@@ -70,12 +74,11 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.server.LocalServices;
-import com.android.server.pm.PackageList;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.AdditionalAnswers;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
@@ -112,7 +115,6 @@
     @Mock private Context mContext;
     @Mock private PackageManager mPackageManager;
     @Mock private INetd mNetdService;
-    @Mock private PackageManagerInternal mMockPmi;
     @Mock private UserManager mUserManager;
     @Mock private PermissionMonitor.Dependencies mDeps;
     @Mock private SystemConfigManager mSystemConfigManager;
@@ -131,16 +133,14 @@
         when(mContext.getSystemService(Context.SYSTEM_CONFIG_SERVICE))
                 .thenReturn(mSystemConfigManager);
         when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]);
+        final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mContext));
+        doReturn(UserHandle.ALL).when(asUserCtx).getUser();
+        when(mContext.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx);
 
         mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps));
 
-        LocalServices.removeServiceForTest(PackageManagerInternal.class);
-        LocalServices.addService(PackageManagerInternal.class, mMockPmi);
-        when(mMockPmi.getPackageList(any())).thenReturn(new PackageList(new ArrayList<String>(),
-                  /* observer */ null));
         when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(/* empty app list */ null);
         mPermissionMonitor.startMonitoring();
-        verify(mMockPmi).getPackageList(mPermissionMonitor);
     }
 
     private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion, int uid,
@@ -770,4 +770,32 @@
                 INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS,
                 new int[]{ MOCK_UID2 });
     }
+
+    @Test
+    public void testIntentReceiver() throws Exception {
+        final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
+        final ArgumentCaptor<BroadcastReceiver> receiverCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        verify(mContext, times(1)).registerReceiver(receiverCaptor.capture(), any(), any(), any());
+        final BroadcastReceiver receiver = receiverCaptor.getValue();
+
+        // Verify receiving PACKAGE_ADDED intent.
+        final Intent addedIntent = new Intent(Intent.ACTION_PACKAGE_ADDED,
+                Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */));
+        addedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1);
+        setPackagePermissions(MOCK_PACKAGE1, MOCK_UID1,
+                new String[] { INTERNET, UPDATE_DEVICE_STATS });
+        receiver.onReceive(mContext, addedIntent);
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+                | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[] { MOCK_UID1 });
+
+        // Verify receiving PACKAGE_REMOVED intent.
+        when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(null);
+        final Intent removedIntent = new Intent(Intent.ACTION_PACKAGE_REMOVED,
+                Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */));
+        removedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1);
+        receiver.onReceive(mContext, removedIntent);
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[] { MOCK_UID1 });
+    }
+
 }
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index b8f7fbc..11fcea6 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -1026,7 +1026,11 @@
             .thenReturn(new Network[] { new Network(101) });
 
         when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(),
-                anyInt(), any(), anyInt())).thenReturn(new Network(102));
+                any(), any(), anyInt())).thenAnswer(invocation -> {
+                    // The runner has registered an agent and is now ready.
+                    legacyRunnerReady.open();
+                    return new Network(102);
+                });
         final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile);
         final TestDeps deps = (TestDeps) vpn.mDeps;
         try {
@@ -1048,7 +1052,7 @@
             ArgumentCaptor<NetworkCapabilities> ncCaptor =
                     ArgumentCaptor.forClass(NetworkCapabilities.class);
             verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(),
-                    lpCaptor.capture(), ncCaptor.capture(), anyInt(), any(), anyInt());
+                    lpCaptor.capture(), ncCaptor.capture(), any(), any(), anyInt());
 
             // In this test the expected address is always v4 so /32.
             // Note that the interface needs to be specified because RouteInfo objects stored in