Merge "Update state in NetworkInfo when network resumes"
diff --git a/core/java/android/net/CaptivePortal.java b/core/java/android/net/CaptivePortal.java
index fb35b4b..8afeb30 100644
--- a/core/java/android/net/CaptivePortal.java
+++ b/core/java/android/net/CaptivePortal.java
@@ -15,7 +15,9 @@
  */
 package android.net;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.os.IBinder;
@@ -23,6 +25,8 @@
 import android.os.Parcelable;
 import android.os.RemoteException;
 
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
 /**
  * A class allowing apps handling the {@link ConnectivityManager#ACTION_CAPTIVE_PORTAL_SIGN_IN}
  * activity to indicate to the system different outcomes of captive portal sign in.  This class is
@@ -76,6 +80,17 @@
     private final IBinder mBinder;
 
     /** @hide */
+    @IntDef(value = {
+        MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY,
+        MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_DISMISSED,
+        MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_UNWANTED,
+        MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_WANTED_AS_IS,
+        MetricsEvent.CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR,
+    })
+    public @interface EventId {
+    }
+
+    /** @hide */
     public CaptivePortal(@NonNull IBinder binder) {
         mBinder = binder;
     }
@@ -153,6 +168,7 @@
      */
     @SystemApi
     @TestApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
     public void reevaluateNetwork() {
         try {
             ICaptivePortal.Stub.asInterface(mBinder).appRequest(APP_REQUEST_REEVALUATION_REQUIRED);
@@ -168,7 +184,7 @@
      */
     @SystemApi
     @TestApi
-    public void logEvent(int eventId, @NonNull String packageName) {
+    public void logEvent(@EventId int eventId, @NonNull String packageName) {
         try {
             ICaptivePortal.Stub.asInterface(mBinder).logEvent(eventId, packageName);
         } catch (RemoteException e) {
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index 3c39d15..d009144 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -252,8 +252,8 @@
                 @NonNull PersistableBundle additionalInfo) {
             mNetwork = network;
             mReportTimestamp = reportTimestamp;
-            mLinkProperties = linkProperties;
-            mNetworkCapabilities = networkCapabilities;
+            mLinkProperties = new LinkProperties(linkProperties);
+            mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
             mAdditionalInfo = additionalInfo;
         }
 
@@ -433,6 +433,12 @@
         /** The detection method used to identify the suspected data stall */
         @DetectionMethod private final int mDetectionMethod;
 
+        /** LinkProperties available on the Network at the reported timestamp */
+        @NonNull private final LinkProperties mLinkProperties;
+
+        /** NetworkCapabilities available on the Network at the reported timestamp */
+        @NonNull private final NetworkCapabilities mNetworkCapabilities;
+
         /** PersistableBundle that may contain additional information on the suspected data stall */
         @NonNull private final PersistableBundle mStallDetails;
 
@@ -446,16 +452,23 @@
          * @param network The Network for which this DataStallReport applies
          * @param reportTimestamp The timestamp for the report
          * @param detectionMethod The detection method used to identify this data stall
+         * @param linkProperties The LinkProperties available on network at reportTimestamp
+         * @param networkCapabilities The NetworkCapabilities available on network at
+         *     reportTimestamp
          * @param stallDetails A PersistableBundle that may contain additional info about the report
          */
         public DataStallReport(
                 @NonNull Network network,
                 long reportTimestamp,
                 @DetectionMethod int detectionMethod,
+                @NonNull LinkProperties linkProperties,
+                @NonNull NetworkCapabilities networkCapabilities,
                 @NonNull PersistableBundle stallDetails) {
             mNetwork = network;
             mReportTimestamp = reportTimestamp;
             mDetectionMethod = detectionMethod;
+            mLinkProperties = new LinkProperties(linkProperties);
+            mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
             mStallDetails = stallDetails;
         }
 
@@ -488,6 +501,26 @@
         }
 
         /**
+         * Returns the LinkProperties available when this report was taken.
+         *
+         * @return LinkProperties available on the Network at the reported timestamp
+         */
+        @NonNull
+        public LinkProperties getLinkProperties() {
+            return new LinkProperties(mLinkProperties);
+        }
+
+        /**
+         * Returns the NetworkCapabilities when this report was taken.
+         *
+         * @return NetworkCapabilities available on the Network at the reported timestamp
+         */
+        @NonNull
+        public NetworkCapabilities getNetworkCapabilities() {
+            return new NetworkCapabilities(mNetworkCapabilities);
+        }
+
+        /**
          * Returns a PersistableBundle with additional info for this report.
          *
          * <p>Gets a bundle with details about the suspected data stall including information
@@ -513,12 +546,20 @@
             return mReportTimestamp == that.mReportTimestamp
                     && mDetectionMethod == that.mDetectionMethod
                     && mNetwork.equals(that.mNetwork)
+                    && mLinkProperties.equals(that.mLinkProperties)
+                    && mNetworkCapabilities.equals(that.mNetworkCapabilities)
                     && persistableBundleEquals(mStallDetails, that.mStallDetails);
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(mNetwork, mReportTimestamp, mDetectionMethod, mStallDetails);
+            return Objects.hash(
+                    mNetwork,
+                    mReportTimestamp,
+                    mDetectionMethod,
+                    mLinkProperties,
+                    mNetworkCapabilities,
+                    mStallDetails);
         }
 
         /** {@inheritDoc} */
@@ -533,6 +574,8 @@
             dest.writeParcelable(mNetwork, flags);
             dest.writeLong(mReportTimestamp);
             dest.writeInt(mDetectionMethod);
+            dest.writeParcelable(mLinkProperties, flags);
+            dest.writeParcelable(mNetworkCapabilities, flags);
             dest.writeParcelable(mStallDetails, flags);
         }
 
@@ -544,6 +587,8 @@
                                 in.readParcelable(null),
                                 in.readLong(),
                                 in.readInt(),
+                                in.readParcelable(null),
+                                in.readParcelable(null),
                                 in.readParcelable(null));
                     }
 
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 94eda01..cb31404 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3341,7 +3341,6 @@
     @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
     public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
             NetworkCapabilities nc, int score, NetworkAgentConfig config, int providerId) {
-
         try {
             return mService.registerNetworkAgent(messenger, ni, lp, nc, score, config, providerId);
         } catch (RemoteException e) {
@@ -3747,6 +3746,7 @@
         checkCallbackNotNull(callback);
         Preconditions.checkArgument(action == REQUEST || need != null, "null NetworkCapabilities");
         final NetworkRequest request;
+        final String callingPackageName = mContext.getOpPackageName();
         try {
             synchronized(sCallbacks) {
                 if (callback.networkRequest != null
@@ -3758,10 +3758,11 @@
                 Messenger messenger = new Messenger(handler);
                 Binder binder = new Binder();
                 if (action == LISTEN) {
-                    request = mService.listenForNetwork(need, messenger, binder);
+                    request = mService.listenForNetwork(
+                            need, messenger, binder, callingPackageName);
                 } else {
                     request = mService.requestNetwork(
-                            need, messenger, timeoutMs, binder, legacyType);
+                            need, messenger, timeoutMs, binder, legacyType, callingPackageName);
                 }
                 if (request != null) {
                     sCallbacks.put(request, callback);
@@ -4034,8 +4035,10 @@
             @NonNull PendingIntent operation) {
         printStackTrace();
         checkPendingIntentNotNull(operation);
+        final String callingPackageName = mContext.getOpPackageName();
         try {
-            mService.pendingRequestForNetwork(request.networkCapabilities, operation);
+            mService.pendingRequestForNetwork(
+                    request.networkCapabilities, operation, callingPackageName);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -4147,8 +4150,10 @@
             @NonNull PendingIntent operation) {
         printStackTrace();
         checkPendingIntentNotNull(operation);
+        final String callingPackageName = mContext.getOpPackageName();
         try {
-            mService.pendingListenForNetwork(request.networkCapabilities, operation);
+            mService.pendingListenForNetwork(
+                    request.networkCapabilities, operation, callingPackageName);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index c871c45..3a55461 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -166,18 +166,19 @@
             in int factorySerialNumber);
 
     NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities,
-            in Messenger messenger, int timeoutSec, in IBinder binder, int legacy);
+            in Messenger messenger, int timeoutSec, in IBinder binder, int legacy,
+            String callingPackageName);
 
     NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities,
-            in PendingIntent operation);
+            in PendingIntent operation, String callingPackageName);
 
     void releasePendingNetworkRequest(in PendingIntent operation);
 
     NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities,
-            in Messenger messenger, in IBinder binder);
+            in Messenger messenger, in IBinder binder, String callingPackageName);
 
     void pendingListenForNetwork(in NetworkCapabilities networkCapabilities,
-            in PendingIntent operation);
+            in PendingIntent operation, String callingPackageName);
 
     void releaseNetworkRequest(in NetworkRequest networkRequest);
 
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index aae9fd4..61a1484 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -262,32 +262,60 @@
      */
     public static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER = BASE + 17;
 
-    // TODO : remove these two constructors. They are a stopgap measure to help sheperding a number
-    // of dependent changes that would conflict throughout the automerger graph. Having these
-    // temporarily helps with the process of going through with all these dependent changes across
-    // the entire tree.
-    /** @hide TODO: decide which of these to expose. */
+    /** @hide TODO: remove and replace usage with the public constructor. */
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
             NetworkCapabilities nc, LinkProperties lp, int score) {
         this(looper, context, logTag, ni, nc, lp, score, null, NetworkProvider.ID_NONE);
     }
 
-    /** @hide TODO: decide which of these to expose. */
+    /** @hide TODO: remove and replace usage with the public constructor. */
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
             NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config) {
         this(looper, context, logTag, ni, nc, lp, score, config, NetworkProvider.ID_NONE);
     }
 
-    /** @hide TODO: decide which of these to expose. */
+    /** @hide TODO: remove and replace usage with the public constructor. */
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
             NetworkCapabilities nc, LinkProperties lp, int score, int providerId) {
         this(looper, context, logTag, ni, nc, lp, score, null, providerId);
     }
 
-    /** @hide TODO: decide which of these to expose. */
+    /** @hide TODO: remove and replace usage with the public constructor. */
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
             NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config,
             int providerId) {
+        this(looper, context, logTag, nc, lp, score, config, providerId, ni);
+    }
+
+    private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
+        // The subtype can be changed with (TODO) setLegacySubtype, but it starts
+        // with the type and an empty description.
+        return new NetworkInfo(config.legacyType, config.legacyType, config.legacyTypeName, "");
+    }
+
+    /**
+     * Create a new network agent.
+     * @param context a {@link Context} to get system services from.
+     * @param looper the {@link Looper} on which to invoke the callbacks.
+     * @param logTag the tag for logs
+     * @param nc the initial {@link NetworkCapabilities} of this network. Update with
+     *           sendNetworkCapabilities.
+     * @param lp the initial {@link LinkProperties} of this network. Update with sendLinkProperties.
+     * @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.
+     */
+    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(looper, context, logTag, nc, lp, score, config,
+                provider == null ? NetworkProvider.ID_NONE : provider.getProviderId(),
+                getLegacyNetworkInfo(config));
+    }
+
+    private NetworkAgent(Looper looper, Context context, String logTag, NetworkCapabilities nc,
+            LinkProperties lp, int score, NetworkAgentConfig config, int providerId,
+            NetworkInfo ni) {
         mHandler = new NetworkAgentHandler(looper);
         LOG_TAG = logTag;
         mContext = context;
diff --git a/core/java/android/net/NetworkAgentConfig.java b/core/java/android/net/NetworkAgentConfig.java
index abc6b67..2c5a113 100644
--- a/core/java/android/net/NetworkAgentConfig.java
+++ b/core/java/android/net/NetworkAgentConfig.java
@@ -21,12 +21,10 @@
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.text.TextUtils;
 
 /**
  * Allows a network transport to provide the system with policy and configuration information about
- * a particular network when registering a {@link NetworkAgent}. This information cannot change once
- * the agent is registered.
+ * a particular network when registering a {@link NetworkAgent}. This information cannot change once the agent is registered.
  *
  * @hide
  */
@@ -120,6 +118,19 @@
     }
 
     /**
+     * The legacy type of this network agent, or TYPE_NONE if unset.
+     * @hide
+     */
+    public int legacyType = ConnectivityManager.TYPE_NONE;
+
+    /**
+     * @return the legacy type
+     */
+    public int getLegacyType() {
+        return legacyType;
+    }
+
+    /**
      * Set to true if the PRIVATE_DNS_BROKEN notification has shown for this network.
      * Reset this bit when private DNS mode is changed from strict mode to opportunistic/off mode.
      *
@@ -127,6 +138,21 @@
      */
     public boolean hasShownBroken;
 
+    /**
+     * The name of the legacy network type. It's a free-form string used in logging.
+     * @hide
+     */
+    @NonNull
+    public String legacyTypeName = "";
+
+    /**
+     * @return the name of the legacy network type. It's a free-form string used in logging.
+     */
+    @NonNull
+    public String getLegacyTypeName() {
+        return legacyTypeName;
+    }
+
     /** @hide */
     public NetworkAgentConfig() {
     }
@@ -140,6 +166,8 @@
             subscriberId = nac.subscriberId;
             provisioningNotificationDisabled = nac.provisioningNotificationDisabled;
             skip464xlat = nac.skip464xlat;
+            legacyType = nac.legacyType;
+            legacyTypeName = nac.legacyTypeName;
         }
     }
 
@@ -185,6 +213,29 @@
         }
 
         /**
+         * Sets the legacy type for this network.
+         *
+         * @param legacyType the type
+         * @return this builder, to facilitate chaining.
+         */
+        @NonNull
+        public Builder setLegacyType(int legacyType) {
+            mConfig.legacyType = legacyType;
+            return this;
+        }
+
+        /**
+         * Sets the name of the legacy type of the agent. It's a free-form string used in logging.
+         * @param legacyTypeName the name
+         * @return this builder, to facilitate chaining.
+         */
+        @NonNull
+        public Builder setLegacyTypeName(@NonNull String legacyTypeName) {
+            mConfig.legacyTypeName = legacyTypeName;
+            return this;
+        }
+
+        /**
          * Returns the constructed {@link NetworkAgentConfig} object.
          */
         @NonNull
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 38f7390..ef4a9e5 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -27,6 +27,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.Process;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.proto.ProtoOutputStream;
 
@@ -63,6 +64,16 @@
     // Set to true when private DNS is broken.
     private boolean mPrivateDnsBroken;
 
+    /**
+     * Uid of the app making the request.
+     */
+    private int mRequestorUid;
+
+    /**
+     * Package name of the app making the request.
+     */
+    private String mRequestorPackageName;
+
     public NetworkCapabilities() {
         clearAll();
         mNetworkCapabilities = DEFAULT_CAPABILITIES;
@@ -89,6 +100,8 @@
         mOwnerUid = Process.INVALID_UID;
         mSSID = null;
         mPrivateDnsBroken = false;
+        mRequestorUid = Process.INVALID_UID;
+        mRequestorPackageName = null;
     }
 
     /**
@@ -109,6 +122,8 @@
         mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities;
         mSSID = nc.mSSID;
         mPrivateDnsBroken = nc.mPrivateDnsBroken;
+        mRequestorUid = nc.mRequestorUid;
+        mRequestorPackageName = nc.mRequestorPackageName;
     }
 
     /**
@@ -810,7 +825,7 @@
     }
 
     /**
-     * UID of the app that owns this network, or INVALID_UID if none/unknown.
+     * UID of the app that owns this network, or Process#INVALID_UID if none/unknown.
      *
      * <p>This field keeps track of the UID of the app that created this network and is in charge of
      * its lifecycle. This could be the UID of apps such as the Wifi network suggestor, the running
@@ -821,8 +836,9 @@
     /**
      * Set the UID of the owner app.
      */
-    public void setOwnerUid(final int uid) {
+    public @NonNull NetworkCapabilities setOwnerUid(final int uid) {
         mOwnerUid = uid;
+        return this;
     }
 
     /**
@@ -865,9 +881,11 @@
      * @hide
      */
     @SystemApi
-    public void setAdministratorUids(@NonNull final List<Integer> administratorUids) {
+    public @NonNull NetworkCapabilities setAdministratorUids(
+            @NonNull final List<Integer> administratorUids) {
         mAdministratorUids.clear();
         mAdministratorUids.addAll(administratorUids);
+        return this;
     }
 
     /**
@@ -1385,6 +1403,7 @@
         combineSignalStrength(nc);
         combineUids(nc);
         combineSSIDs(nc);
+        combineRequestor(nc);
     }
 
     /**
@@ -1404,7 +1423,8 @@
                 && satisfiedBySpecifier(nc)
                 && (onlyImmutable || satisfiedBySignalStrength(nc))
                 && (onlyImmutable || satisfiedByUids(nc))
-                && (onlyImmutable || satisfiedBySSID(nc)));
+                && (onlyImmutable || satisfiedBySSID(nc)))
+                && (onlyImmutable || satisfiedByRequestor(nc));
     }
 
     /**
@@ -1488,7 +1508,7 @@
     public boolean equals(@Nullable Object obj) {
         if (obj == null || (obj instanceof NetworkCapabilities == false)) return false;
         NetworkCapabilities that = (NetworkCapabilities) obj;
-        return (equalsNetCapabilities(that)
+        return equalsNetCapabilities(that)
                 && equalsTransportTypes(that)
                 && equalsLinkBandwidths(that)
                 && equalsSignalStrength(that)
@@ -1496,7 +1516,8 @@
                 && equalsTransportInfo(that)
                 && equalsUids(that)
                 && equalsSSID(that)
-                && equalsPrivateDnsBroken(that));
+                && equalsPrivateDnsBroken(that)
+                && equalsRequestor(that);
     }
 
     @Override
@@ -1514,7 +1535,9 @@
                 + Objects.hashCode(mUids) * 31
                 + Objects.hashCode(mSSID) * 37
                 + Objects.hashCode(mTransportInfo) * 41
-                + Objects.hashCode(mPrivateDnsBroken) * 43;
+                + Objects.hashCode(mPrivateDnsBroken) * 43
+                + Objects.hashCode(mRequestorUid) * 47
+                + Objects.hashCode(mRequestorPackageName) * 53;
     }
 
     @Override
@@ -1537,6 +1560,8 @@
         dest.writeBoolean(mPrivateDnsBroken);
         dest.writeList(mAdministratorUids);
         dest.writeInt(mOwnerUid);
+        dest.writeInt(mRequestorUid);
+        dest.writeString(mRequestorPackageName);
     }
 
     public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -1559,6 +1584,8 @@
                 netCap.mPrivateDnsBroken = in.readBoolean();
                 netCap.setAdministratorUids(in.readArrayList(null));
                 netCap.mOwnerUid = in.readInt();
+                netCap.mRequestorUid = in.readInt();
+                netCap.mRequestorPackageName = in.readString();
                 return netCap;
             }
             @Override
@@ -1624,6 +1651,9 @@
             sb.append(" Private DNS is broken");
         }
 
+        sb.append(" RequestorUid: ").append(mRequestorUid);
+        sb.append(" RequestorPackageName: ").append(mRequestorPackageName);
+
         sb.append("]");
         return sb.toString();
     }
@@ -1632,6 +1662,7 @@
     private interface NameOf {
         String nameOf(int value);
     }
+
     /**
      * @hide
      */
@@ -1799,4 +1830,120 @@
     private boolean equalsPrivateDnsBroken(NetworkCapabilities nc) {
         return mPrivateDnsBroken == nc.mPrivateDnsBroken;
     }
+
+    /**
+     * Set the uid of the app making the request.
+     *
+     * Note: This works only for {@link NetworkAgent} instances. Any capabilities passed in
+     * via the public {@link ConnectivityManager} API's will have this field overwritten.
+     *
+     * @param uid UID of the app.
+     * @hide
+     */
+    @SystemApi
+    public @NonNull NetworkCapabilities setRequestorUid(int uid) {
+        mRequestorUid = uid;
+        return this;
+    }
+
+    /**
+     * @return the uid of the app making the request.
+     *
+     * Note: This could return {@link Process#INVALID_UID} if the {@link NetworkRequest}
+     * object was not obtained from {@link ConnectivityManager}.
+     * @hide
+     */
+    public int getRequestorUid() {
+        return mRequestorUid;
+    }
+
+    /**
+     * Set the package name of the app making the request.
+     *
+     * Note: This works only for {@link NetworkAgent} instances. Any capabilities passed in
+     * via the public {@link ConnectivityManager} API's will have this field overwritten.
+     *
+     * @param packageName package name of the app.
+     * @hide
+     */
+    @SystemApi
+    public @NonNull NetworkCapabilities setRequestorPackageName(@NonNull String packageName) {
+        mRequestorPackageName = packageName;
+        return this;
+    }
+
+    /**
+     * @return the package name of the app making the request.
+     *
+     * Note: This could return {@code null} if the {@link NetworkRequest} object was not obtained
+     * from {@link ConnectivityManager}.
+     * @hide
+     */
+    @Nullable
+    public String getRequestorPackageName() {
+        return mRequestorPackageName;
+    }
+
+    /**
+     * Set the uid and package name of the app making the request.
+     *
+     * Note: This is intended to be only invoked from within connectivitiy service.
+     *
+     * @param uid UID of the app.
+     * @param packageName package name of the app.
+     * @hide
+     */
+    public @NonNull NetworkCapabilities setRequestorUidAndPackageName(
+            int uid, @NonNull String packageName) {
+        return setRequestorUid(uid).setRequestorPackageName(packageName);
+    }
+
+    /**
+     * Test whether the passed NetworkCapabilities satisfies the requestor restrictions of this
+     * capabilities.
+     *
+     * This method is called on the NetworkCapabilities embedded in a request with the
+     * capabilities of an available network. If the available network, sets a specific
+     * requestor (by uid and optionally package name), then this will only match a request from the
+     * same app. If either of the capabilities have an unset uid or package name, then it matches
+     * everything.
+     * <p>
+     * nc is assumed nonnull. Else, NPE.
+     */
+    private boolean satisfiedByRequestor(NetworkCapabilities nc) {
+        // No uid set, matches everything.
+        if (mRequestorUid == Process.INVALID_UID || nc.mRequestorUid == Process.INVALID_UID) {
+            return true;
+        }
+        // uids don't match.
+        if (mRequestorUid != nc.mRequestorUid) return false;
+        // No package names set, matches everything
+        if (null == nc.mRequestorPackageName || null == mRequestorPackageName) return true;
+        // check for package name match.
+        return TextUtils.equals(mRequestorPackageName, nc.mRequestorPackageName);
+    }
+
+    /**
+     * Combine requestor info of the capabilities.
+     * <p>
+     * This is only legal if either the requestor info of this object is reset, or both info are
+     * equal.
+     * nc is assumed nonnull.
+     */
+    private void combineRequestor(@NonNull NetworkCapabilities nc) {
+        if (mRequestorUid != Process.INVALID_UID && mRequestorUid != nc.mOwnerUid) {
+            throw new IllegalStateException("Can't combine two uids");
+        }
+        if (mRequestorPackageName != null
+                && !mRequestorPackageName.equals(nc.mRequestorPackageName)) {
+            throw new IllegalStateException("Can't combine two package names");
+        }
+        setRequestorUid(nc.mRequestorUid);
+        setRequestorPackageName(nc.mRequestorPackageName);
+    }
+
+    private boolean equalsRequestor(NetworkCapabilities nc) {
+        return mRequestorUid == nc.mRequestorUid
+                && TextUtils.equals(mRequestorPackageName, nc.mRequestorPackageName);
+    }
 }
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index ee4379a..b0bf64e 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -380,6 +380,7 @@
         dest.writeInt(requestId);
         dest.writeString(type.name());
     }
+
     public static final @android.annotation.NonNull Creator<NetworkRequest> CREATOR =
         new Creator<NetworkRequest>() {
             public NetworkRequest createFromParcel(Parcel in) {
@@ -494,6 +495,31 @@
         return networkCapabilities.getNetworkSpecifier();
     }
 
+    /**
+     * @return the uid of the app making the request.
+     *
+     * Note: This could return {@link Process#INVALID_UID} if the {@link NetworkRequest} object was
+     * not obtained from {@link ConnectivityManager}.
+     * @hide
+     */
+    @SystemApi
+    public int getRequestorUid() {
+        return networkCapabilities.getRequestorUid();
+    }
+
+    /**
+     * @return the package name of the app making the request.
+     *
+     * Note: This could return {@code null} if the {@link NetworkRequest} object was not obtained
+     * from {@link ConnectivityManager}.
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public String getRequestorPackageName() {
+        return networkCapabilities.getRequestorPackageName();
+    }
+
     public String toString() {
         return "NetworkRequest [ " + type + " id=" + requestId +
                 (legacyType != ConnectivityManager.TYPE_NONE ? ", legacyType=" + legacyType : "") +
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d2b1b72..1da2080 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -155,7 +155,6 @@
 import android.security.KeyStore;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
-import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.LocalLog;
 import android.util.Log;
@@ -195,6 +194,7 @@
 import com.android.server.connectivity.NetworkDiagnostics;
 import com.android.server.connectivity.NetworkNotificationManager;
 import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
+import com.android.server.connectivity.NetworkRanker;
 import com.android.server.connectivity.PermissionMonitor;
 import com.android.server.connectivity.ProxyTracker;
 import com.android.server.connectivity.Vpn;
@@ -231,6 +231,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.SortedSet;
+import java.util.StringJoiner;
 import java.util.TreeSet;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -579,6 +580,7 @@
     final ConnectivityDiagnosticsHandler mConnectivityDiagnosticsHandler;
 
     private final DnsManager mDnsManager;
+    private final NetworkRanker mNetworkRanker;
 
     private boolean mSystemReady;
     private Intent mInitialBroadcast;
@@ -604,7 +606,7 @@
 
     private Set<String> mWolSupportedInterfaces;
 
-    private TelephonyManager mTelephonyManager;
+    private final TelephonyManager mTelephonyManager;
     private final AppOpsManager mAppOpsManager;
 
     private final LocationPermissionChecker mLocationPermissionChecker;
@@ -727,9 +729,9 @@
         private void maybeLogBroadcast(NetworkAgentInfo nai, DetailedState state, int type,
                 boolean isDefaultNetwork) {
             if (DBG) {
-                log("Sending " + state +
-                        " broadcast for type " + type + " " + nai.name() +
-                        " isDefaultNetwork=" + isDefaultNetwork);
+                log("Sending " + state
+                        + " broadcast for type " + type + " " + nai.toShortString()
+                        + " isDefaultNetwork=" + isDefaultNetwork);
             }
         }
 
@@ -809,14 +811,6 @@
             }
         }
 
-        private String naiToString(NetworkAgentInfo nai) {
-            String name = nai.name();
-            String state = (nai.networkInfo != null) ?
-                    nai.networkInfo.getState() + "/" + nai.networkInfo.getDetailedState() :
-                    "???/???";
-            return name + " " + state;
-        }
-
         public void dump(IndentingPrintWriter pw) {
             pw.println("mLegacyTypeTracker:");
             pw.increaseIndent();
@@ -831,7 +825,7 @@
                 for (int type = 0; type < mTypeLists.length; type++) {
                     if (mTypeLists[type] == null || mTypeLists[type].isEmpty()) continue;
                     for (NetworkAgentInfo nai : mTypeLists[type]) {
-                        pw.println(type + " " + naiToString(nai));
+                        pw.println(type + " " + nai.toShortString());
                     }
                 }
             }
@@ -963,9 +957,11 @@
         mDeps = checkNotNull(deps, "missing Dependencies");
         mSystemProperties = mDeps.getSystemProperties();
         mNetIdManager = mDeps.makeNetIdManager();
+        mContext = checkNotNull(context, "missing Context");
 
         mMetricsLog = logger;
         mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST);
+        mNetworkRanker = new NetworkRanker();
         NetworkRequestInfo defaultNRI = new NetworkRequestInfo(null, mDefaultRequest, new Binder());
         mNetworkRequests.put(mDefaultRequest, defaultNRI);
         mNetworkRequestInfoLogs.log("REGISTER " + defaultNRI);
@@ -991,7 +987,6 @@
 
         mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
 
-        mContext = checkNotNull(context, "missing Context");
         mNMS = checkNotNull(netManager, "missing INetworkManagementService");
         mStatsService = checkNotNull(statsService, "missing INetworkStatsService");
         mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
@@ -1171,6 +1166,7 @@
             int transportType, NetworkRequest.Type type) {
         final NetworkCapabilities netCap = new NetworkCapabilities();
         netCap.addCapability(NET_CAPABILITY_INTERNET);
+        netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName());
         if (transportType > -1) {
             netCap.addTransportType(transportType);
         }
@@ -1701,10 +1697,12 @@
         return newLp;
     }
 
-    private void restrictRequestUidsForCaller(NetworkCapabilities nc) {
+    private void restrictRequestUidsForCallerAndSetRequestorInfo(NetworkCapabilities nc,
+            int callerUid, String callerPackageName) {
         if (!checkSettingsPermission()) {
-            nc.setSingleUid(Binder.getCallingUid());
+            nc.setSingleUid(callerUid);
         }
+        nc.setRequestorUidAndPackageName(callerUid, callerPackageName);
         nc.setAdministratorUids(Collections.EMPTY_LIST);
 
         // Clear owner UID; this can never come from an app.
@@ -2786,7 +2784,7 @@
                         nai.everCaptivePortalDetected |= visible;
                         if (nai.lastCaptivePortalDetected &&
                             Settings.Global.CAPTIVE_PORTAL_MODE_AVOID == getCaptivePortalMode()) {
-                            if (DBG) log("Avoiding captive portal network: " + nai.name());
+                            if (DBG) log("Avoiding captive portal network: " + nai.toShortString());
                             nai.asyncChannel.sendMessage(
                                     NetworkAgent.CMD_PREVENT_AUTOMATIC_RECONNECT);
                             teardownUnneededNetwork(nai);
@@ -2845,7 +2843,7 @@
                 final String logMsg = !TextUtils.isEmpty(redirectUrl)
                         ? " with redirect to " + redirectUrl
                         : "";
-                log(nai.name() + " validation " + (valid ? "passed" : "failed") + logMsg);
+                log(nai.toShortString() + " validation " + (valid ? "passed" : "failed") + logMsg);
             }
             if (valid != nai.lastValidated) {
                 if (wasDefault) {
@@ -3126,13 +3124,13 @@
         //    one lingered request, start lingering.
         nai.updateLingerTimer();
         if (nai.isLingering() && nai.numForegroundNetworkRequests() > 0) {
-            if (DBG) log("Unlingering " + nai.name());
+            if (DBG) log("Unlingering " + nai.toShortString());
             nai.unlinger();
             logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
         } else if (unneeded(nai, UnneededFor.LINGER) && nai.getLingerExpiry() > 0) {
             if (DBG) {
                 final int lingerTime = (int) (nai.getLingerExpiry() - now);
-                log("Lingering " + nai.name() + " for " + lingerTime + "ms");
+                log("Lingering " + nai.toShortString() + " for " + lingerTime + "ms");
             }
             nai.linger();
             logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
@@ -3196,7 +3194,7 @@
     private void disconnectAndDestroyNetwork(NetworkAgentInfo nai) {
         ensureRunningOnConnectivityServiceThread();
         if (DBG) {
-            log(nai.name() + " got DISCONNECTED, was satisfying " + nai.numNetworkRequests());
+            log(nai.toShortString() + " disconnected, was satisfying " + nai.numNetworkRequests());
         }
         // Clear all notifications of this network.
         mNotifier.clearNotification(nai.network.netId);
@@ -3254,7 +3252,7 @@
             mDefaultNetworkNai = null;
             updateDataActivityTracking(null /* newNetwork */, nai);
             notifyLockdownVpn(nai);
-            ensureNetworkTransitionWakelock(nai.name());
+            ensureNetworkTransitionWakelock(nai.toShortString());
         }
         mLegacyTypeTracker.remove(nai, wasDefault);
         if (!nai.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
@@ -3477,8 +3475,8 @@
                 boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
                 nai.removeRequest(nri.request.requestId);
                 if (VDBG || DDBG) {
-                    log(" Removing from current network " + nai.name() +
-                            ", leaving " + nai.numNetworkRequests() + " requests.");
+                    log(" Removing from current network " + nai.toShortString()
+                            + ", leaving " + nai.numNetworkRequests() + " requests.");
                 }
                 // If there are still lingered requests on this network, don't tear it down,
                 // but resume lingering instead.
@@ -3487,7 +3485,7 @@
                     notifyNetworkLosing(nai, now);
                 }
                 if (unneeded(nai, UnneededFor.TEARDOWN)) {
-                    if (DBG) log("no live requests for " + nai.name() + "; disconnecting");
+                    if (DBG) log("no live requests for " + nai.toShortString() + "; disconnecting");
                     teardownUnneededNetwork(nai);
                 } else {
                     wasKept = true;
@@ -3744,6 +3742,7 @@
             if (nm == null) return;
 
             if (request == CaptivePortal.APP_REQUEST_REEVALUATION_REQUIRED) {
+                checkNetworkStackPermission();
                 nm.forceReevaluation(Binder.getCallingUid());
             }
         }
@@ -3822,7 +3821,7 @@
         pw.increaseIndent();
         for (NetworkAgentInfo nai : networksSortedById()) {
             if (nai.avoidUnvalidated) {
-                pw.println(nai.name());
+                pw.println(nai.toShortString());
             }
         }
         pw.decreaseIndent();
@@ -3934,7 +3933,7 @@
 
     private void handleNetworkUnvalidated(NetworkAgentInfo nai) {
         NetworkCapabilities nc = nai.networkCapabilities;
-        if (DBG) log("handleNetworkUnvalidated " + nai.name() + " cap=" + nc);
+        if (DBG) log("handleNetworkUnvalidated " + nai.toShortString() + " cap=" + nc);
 
         if (!nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
             return;
@@ -5309,7 +5308,7 @@
     // This checks that the passed capabilities either do not request a
     // specific SSID/SignalStrength, or the calling app has permission to do so.
     private void ensureSufficientPermissionsForRequest(NetworkCapabilities nc,
-            int callerPid, int callerUid) {
+            int callerPid, int callerUid, String callerPackageName) {
         if (null != nc.getSSID() && !checkSettingsPermission(callerPid, callerUid)) {
             throw new SecurityException("Insufficient permissions to request a specific SSID");
         }
@@ -5319,6 +5318,7 @@
             throw new SecurityException(
                     "Insufficient permissions to request a specific signal strength");
         }
+        mAppOpsManager.checkPackage(callerUid, callerPackageName);
     }
 
     private ArrayList<Integer> getSignalStrengthThresholds(NetworkAgentInfo nai) {
@@ -5348,7 +5348,7 @@
                 detail = reason;
             }
             log(String.format("updateSignalStrengthThresholds: %s, sending %s to %s",
-                    detail, Arrays.toString(thresholdsArray.toArray()), nai.name()));
+                    detail, Arrays.toString(thresholdsArray.toArray()), nai.toShortString()));
         }
 
         nai.asyncChannel.sendMessage(
@@ -5365,7 +5365,6 @@
             return;
         }
         MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(ns);
-        ns.assertValidFromUid(Binder.getCallingUid());
     }
 
     private void ensureValid(NetworkCapabilities nc) {
@@ -5377,7 +5376,9 @@
 
     @Override
     public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
-            Messenger messenger, int timeoutMs, IBinder binder, int legacyType) {
+            Messenger messenger, int timeoutMs, IBinder binder, int legacyType,
+            @NonNull String callingPackageName) {
+        final int callingUid = Binder.getCallingUid();
         final NetworkRequest.Type type = (networkCapabilities == null)
                 ? NetworkRequest.Type.TRACK_DEFAULT
                 : NetworkRequest.Type.REQUEST;
@@ -5385,7 +5386,7 @@
         // the default network request. This allows callers to keep track of
         // the system default network.
         if (type == NetworkRequest.Type.TRACK_DEFAULT) {
-            networkCapabilities = createDefaultNetworkCapabilitiesForUid(Binder.getCallingUid());
+            networkCapabilities = createDefaultNetworkCapabilitiesForUid(callingUid);
             enforceAccessPermission();
         } else {
             networkCapabilities = new NetworkCapabilities(networkCapabilities);
@@ -5397,13 +5398,14 @@
         }
         ensureRequestableCapabilities(networkCapabilities);
         ensureSufficientPermissionsForRequest(networkCapabilities,
-                Binder.getCallingPid(), Binder.getCallingUid());
+                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.
         // 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.
-        restrictRequestUidsForCaller(networkCapabilities);
+        restrictRequestUidsForCallerAndSetRequestorInfo(networkCapabilities,
+                callingUid, callingPackageName);
 
         if (timeoutMs < 0) {
             throw new IllegalArgumentException("Bad timeout specified");
@@ -5478,16 +5480,18 @@
 
     @Override
     public NetworkRequest pendingRequestForNetwork(NetworkCapabilities networkCapabilities,
-            PendingIntent operation) {
+            PendingIntent operation, @NonNull String callingPackageName) {
         checkNotNull(operation, "PendingIntent cannot be null.");
+        final int callingUid = Binder.getCallingUid();
         networkCapabilities = new NetworkCapabilities(networkCapabilities);
         enforceNetworkRequestPermissions(networkCapabilities);
         enforceMeteredApnPolicy(networkCapabilities);
         ensureRequestableCapabilities(networkCapabilities);
         ensureSufficientPermissionsForRequest(networkCapabilities,
-                Binder.getCallingPid(), Binder.getCallingUid());
+                Binder.getCallingPid(), callingUid, callingPackageName);
         ensureValidNetworkSpecifier(networkCapabilities);
-        restrictRequestUidsForCaller(networkCapabilities);
+        restrictRequestUidsForCallerAndSetRequestorInfo(networkCapabilities,
+                callingUid, callingPackageName);
 
         NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
                 nextNetworkRequestId(), NetworkRequest.Type.REQUEST);
@@ -5535,15 +5539,16 @@
 
     @Override
     public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
-            Messenger messenger, IBinder binder) {
+            Messenger messenger, IBinder binder, @NonNull String callingPackageName) {
+        final int callingUid = Binder.getCallingUid();
         if (!hasWifiNetworkListenPermission(networkCapabilities)) {
             enforceAccessPermission();
         }
 
         NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
         ensureSufficientPermissionsForRequest(networkCapabilities,
-                Binder.getCallingPid(), Binder.getCallingUid());
-        restrictRequestUidsForCaller(nc);
+                Binder.getCallingPid(), callingUid, callingPackageName);
+        restrictRequestUidsForCallerAndSetRequestorInfo(nc, callingUid, callingPackageName);
         // Apps without the CHANGE_NETWORK_STATE permission can't use background networks, so
         // make all their listens include NET_CAPABILITY_FOREGROUND. That way, they will get
         // onLost and onAvailable callbacks when networks move in and out of the background.
@@ -5563,17 +5568,17 @@
 
     @Override
     public void pendingListenForNetwork(NetworkCapabilities networkCapabilities,
-            PendingIntent operation) {
+            PendingIntent operation, @NonNull String callingPackageName) {
         checkNotNull(operation, "PendingIntent cannot be null.");
+        final int callingUid = Binder.getCallingUid();
         if (!hasWifiNetworkListenPermission(networkCapabilities)) {
             enforceAccessPermission();
         }
         ensureValid(networkCapabilities);
         ensureSufficientPermissionsForRequest(networkCapabilities,
-                Binder.getCallingPid(), Binder.getCallingUid());
-
+                Binder.getCallingPid(), callingUid, callingPackageName);
         final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
-        restrictRequestUidsForCaller(nc);
+        restrictRequestUidsForCallerAndSetRequestorInfo(nc, callingUid, callingPackageName);
 
         NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
                 NetworkRequest.Type.LISTEN);
@@ -6272,9 +6277,9 @@
         // newLp is already a defensive copy.
         newLp.ensureDirectlyConnectedRoutes();
         if (VDBG || DDBG) {
-            log("Update of LinkProperties for " + nai.name() +
-                    "; created=" + nai.created +
-                    "; everConnected=" + nai.everConnected);
+            log("Update of LinkProperties for " + nai.toShortString()
+                    + "; created=" + nai.created
+                    + "; everConnected=" + nai.everConnected);
         }
         updateLinkProperties(nai, newLp, new LinkProperties(nai.linkProperties));
     }
@@ -6444,7 +6449,7 @@
             loge("Unknown NetworkAgentInfo in handleLingerComplete");
             return;
         }
-        if (DBG) log("handleLingerComplete for " + oldNetwork.name());
+        if (DBG) log("handleLingerComplete for " + oldNetwork.toShortString());
 
         // If we get here it means that the last linger timeout for this network expired. So there
         // must be no other active linger timers, and we must stop lingering.
@@ -6517,18 +6522,7 @@
     }
 
     // An accumulator class to gather the list of changes that result from a rematch.
-    // TODO : enrich to represent an entire set of changes to apply.
     private static class NetworkReassignment {
-        static class NetworkBgStatePair {
-            @NonNull final NetworkAgentInfo mNetwork;
-            final boolean mOldBackground;
-            NetworkBgStatePair(@NonNull final NetworkAgentInfo network,
-                    final boolean oldBackground) {
-                mNetwork = network;
-                mOldBackground = oldBackground;
-            }
-        }
-
         static class RequestReassignment {
             @NonNull public final NetworkRequestInfo mRequest;
             @Nullable public final NetworkAgentInfo mOldNetwork;
@@ -6540,44 +6534,34 @@
                 mOldNetwork = oldNetwork;
                 mNewNetwork = newNetwork;
             }
+
+            public String toString() {
+                return mRequest.request.requestId + " : "
+                        + (null != mOldNetwork ? mOldNetwork.network.netId : "null")
+                        + " → " + (null != mNewNetwork ? mNewNetwork.network.netId : "null");
+            }
         }
 
-        @NonNull private final Set<NetworkBgStatePair> mRematchedNetworks = new ArraySet<>();
-        @NonNull private final Map<NetworkRequestInfo, RequestReassignment> mReassignments =
-                new ArrayMap<>();
-
-        @NonNull Iterable<NetworkBgStatePair> getRematchedNetworks() {
-            return mRematchedNetworks;
-        }
+        @NonNull private final ArrayList<RequestReassignment> mReassignments = new ArrayList<>();
 
         @NonNull Iterable<RequestReassignment> getRequestReassignments() {
-            return mReassignments.values();
+            return mReassignments;
         }
 
         void addRequestReassignment(@NonNull final RequestReassignment reassignment) {
-            final RequestReassignment oldChange = mReassignments.get(reassignment.mRequest);
-            if (null == oldChange) {
-                mReassignments.put(reassignment.mRequest, reassignment);
-                return;
+            if (!Build.IS_USER) {
+                // The code is never supposed to add two reassignments of the same request. Make
+                // sure this stays true, but without imposing this expensive check on all
+                // reassignments on all user devices.
+                for (final RequestReassignment existing : mReassignments) {
+                    if (existing.mRequest.equals(reassignment.mRequest)) {
+                        throw new IllegalStateException("Trying to reassign ["
+                                + reassignment + "] but already have ["
+                                + existing + "]");
+                    }
+                }
             }
-            if (oldChange.mNewNetwork != reassignment.mOldNetwork) {
-                throw new IllegalArgumentException("Reassignment <" + reassignment.mRequest + "> ["
-                        + reassignment.mOldNetwork + " -> " + reassignment.mNewNetwork
-                        + "] conflicts with ["
-                        + oldChange.mOldNetwork + " -> " + oldChange.mNewNetwork + "]");
-            }
-            // There was already a note to reassign this request from a network A to a network B,
-            // and a reassignment is added from network B to some other network C. The following
-            // synthesizes the merged reassignment that goes A -> C. An interesting (but not
-            // special) case to think about is when B is null, which can happen when the rematch
-            // loop notices the current satisfier doesn't satisfy the request any more, but
-            // hasn't yet encountered another network that could.
-            mReassignments.put(reassignment.mRequest, new RequestReassignment(reassignment.mRequest,
-                    oldChange.mOldNetwork, reassignment.mNewNetwork));
-        }
-
-        void addRematchedNetwork(@NonNull final NetworkBgStatePair network) {
-            mRematchedNetworks.add(network);
+            mReassignments.add(reassignment);
         }
 
         // Will return null if this reassignment does not change the network assigned to
@@ -6589,98 +6573,25 @@
             }
             return null;
         }
-    }
 
-    // TODO : remove this when it's useless
-    @NonNull private NetworkReassignment computeInitialReassignment() {
-        final NetworkReassignment change = new NetworkReassignment();
-        for (NetworkRequestInfo nri : mNetworkRequests.values()) {
-            change.addRequestReassignment(new NetworkReassignment.RequestReassignment(nri,
-                    nri.mSatisfier, nri.mSatisfier));
-        }
-        return change;
-    }
-
-    private ArrayMap<NetworkRequestInfo, NetworkAgentInfo> computeRequestReassignmentForNetwork(
-            @NonNull final NetworkReassignment changes,
-            @NonNull final NetworkAgentInfo newNetwork) {
-        final int score = newNetwork.getCurrentScore();
-        final ArrayMap<NetworkRequestInfo, NetworkAgentInfo> reassignedRequests = new ArrayMap<>();
-        for (NetworkRequestInfo nri : mNetworkRequests.values()) {
-            // Process requests in the first pass and listens in the second pass. This allows us to
-            // change a network's capabilities depending on which requests it has. This is only
-            // correct if the change in capabilities doesn't affect whether the network satisfies
-            // requests or not, and doesn't affect the network's score.
-            if (nri.request.isListen()) continue;
-
-            // The reassignment has been seeded with the initial assignment, therefore
-            // getReassignment can't be null and mNewNetwork is only null if there was no
-            // satisfier in the first place or there was an explicit reassignment to null.
-            final NetworkAgentInfo currentNetwork = changes.getReassignment(nri).mNewNetwork;
-            final boolean satisfies = newNetwork.satisfies(nri.request);
-            if (newNetwork == currentNetwork && satisfies) continue;
-
-            // check if it satisfies the NetworkCapabilities
-            if (VDBG) log("  checking if request is satisfied: " + nri.request);
-            if (satisfies) {
-                // next check if it's better than any current network we're using for
-                // this request
-                if (VDBG || DDBG) {
-                    log("currentScore = "
-                            + (currentNetwork != null ? currentNetwork.getCurrentScore() : 0)
-                            + ", newScore = " + score);
-                }
-                if (currentNetwork == null || currentNetwork.getCurrentScore() < score) {
-                    reassignedRequests.put(nri, newNetwork);
-                    changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
-                            nri, currentNetwork, newNetwork));
-                }
-            } else if (newNetwork == currentNetwork) {
-                reassignedRequests.put(nri, null);
-                changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
-                        nri, currentNetwork, null));
+        public String toString() {
+            final StringJoiner sj = new StringJoiner(", " /* delimiter */,
+                    "NetReassign [" /* prefix */, "]" /* suffix */);
+            if (mReassignments.isEmpty()) return sj.add("no changes").toString();
+            for (final RequestReassignment rr : getRequestReassignments()) {
+                sj.add(rr.toString());
             }
+            return sj.toString();
         }
 
-        return reassignedRequests;
-    }
-
-    // Handles a network appearing or improving its score.
-    //
-    // - Evaluates all current NetworkRequests that can be
-    //   satisfied by newNetwork, and reassigns to newNetwork
-    //   any such requests for which newNetwork is the best.
-    //
-    // - Writes into the passed reassignment object all changes that should be done for
-    //   rematching this network with all requests, to be applied later.
-    //
-    // TODO : stop writing to the passed reassignment. This is temporarily more useful, but
-    // it's unidiomatic Java and it's hard to read.
-    //
-    // @param changes a currently-building list of changes to write to
-    // @param newNetwork is the network to be matched against NetworkRequests.
-    // @param now the time the rematch starts, as returned by SystemClock.elapsedRealtime();
-    private void rematchNetworkAndRequests(@NonNull final NetworkReassignment changes,
-            @NonNull final NetworkAgentInfo newNetwork, final long now) {
-        ensureRunningOnConnectivityServiceThread();
-        if (!newNetwork.everConnected) return;
-
-        changes.addRematchedNetwork(new NetworkReassignment.NetworkBgStatePair(newNetwork,
-                newNetwork.isBackgroundNetwork()));
-
-        if (VDBG || DDBG) log("rematching " + newNetwork.name());
-
-        final ArrayMap<NetworkRequestInfo, NetworkAgentInfo> reassignedRequests =
-                computeRequestReassignmentForNetwork(changes, newNetwork);
-
-        // Find and migrate to this Network any NetworkRequests for
-        // which this network is now the best.
-        for (final Map.Entry<NetworkRequestInfo, NetworkAgentInfo> entry :
-                reassignedRequests.entrySet()) {
-            final NetworkRequestInfo nri = entry.getKey();
-            final NetworkAgentInfo previousSatisfier = nri.mSatisfier;
-            final NetworkAgentInfo newSatisfier = entry.getValue();
-            updateSatisfiersForRematchRequest(nri, previousSatisfier, newSatisfier, now);
+        public String debugString() {
+            final StringBuilder sb = new StringBuilder();
+            sb.append("NetworkReassignment :");
+            if (mReassignments.isEmpty()) return sb.append(" no changes").toString();
+            for (final RequestReassignment rr : getRequestReassignments()) {
+                sb.append("\n  ").append(rr);
+            }
+            return sb.append("\n").toString();
         }
     }
 
@@ -6689,10 +6600,10 @@
             @Nullable final NetworkAgentInfo newSatisfier,
             final long now) {
         if (newSatisfier != null) {
-            if (VDBG) log("rematch for " + newSatisfier.name());
+            if (VDBG) log("rematch for " + newSatisfier.toShortString());
             if (previousSatisfier != null) {
                 if (VDBG || DDBG) {
-                    log("   accepting network in place of " + previousSatisfier.name());
+                    log("   accepting network in place of " + previousSatisfier.toShortString());
                 }
                 previousSatisfier.removeRequest(nri.request.requestId);
                 previousSatisfier.lingerRequest(nri.request, now, mLingerDelayMs);
@@ -6701,11 +6612,12 @@
             }
             newSatisfier.unlingerRequest(nri.request);
             if (!newSatisfier.addRequest(nri.request)) {
-                Slog.wtf(TAG, "BUG: " + newSatisfier.name() + " already has " + nri.request);
+                Slog.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
+                        + nri.request);
             }
         } else {
             if (DBG) {
-                log("Network " + previousSatisfier.name() + " stopped satisfying"
+                log("Network " + previousSatisfier.toShortString() + " stopped satisfying"
                         + " request " + nri.request.requestId);
             }
             previousSatisfier.removeRequest(nri.request.requestId);
@@ -6713,30 +6625,67 @@
         nri.mSatisfier = newSatisfier;
     }
 
+    @NonNull
+    private NetworkReassignment computeNetworkReassignment() {
+        ensureRunningOnConnectivityServiceThread();
+        final NetworkReassignment changes = new NetworkReassignment();
+
+        // Gather the list of all relevant agents and sort them by score.
+        final ArrayList<NetworkAgentInfo> nais = new ArrayList<>();
+        for (final NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+            if (!nai.everConnected) continue;
+            nais.add(nai);
+        }
+
+        for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
+            if (nri.request.isListen()) continue;
+            final NetworkAgentInfo bestNetwork = mNetworkRanker.getBestNetwork(nri.request, nais);
+            if (bestNetwork != nri.mSatisfier) {
+                // bestNetwork may be null if no network can satisfy this request.
+                changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
+                        nri, nri.mSatisfier, bestNetwork));
+            }
+        }
+        return changes;
+    }
+
     /**
      * Attempt to rematch all Networks with NetworkRequests.  This may result in Networks
      * being disconnected.
      */
     private void rematchAllNetworksAndRequests() {
-        // TODO: This may be slow, and should be optimized. Unfortunately at this moment the
-        // processing is network-major instead of request-major (the code iterates through all
-        // networks, then for each it iterates for all requests), which is a problem for re-scoring
-        // requests. Once the code has switched to a request-major iteration style, this can
-        // be optimized to only do the processing needed.
+        // TODO: This may be slow, and should be optimized.
         final long now = SystemClock.elapsedRealtime();
-        final NetworkAgentInfo oldDefaultNetwork = getDefaultNetwork();
+        final NetworkReassignment changes = computeNetworkReassignment();
+        if (VDBG || DDBG) {
+            log(changes.debugString());
+        } else if (DBG) {
+            log(changes.toString()); // Shorter form, only one line of log
+        }
+        applyNetworkReassignment(changes, now);
+    }
 
-        final NetworkAgentInfo[] nais = mNetworkAgentInfos.values().toArray(
-                new NetworkAgentInfo[mNetworkAgentInfos.size()]);
-        // Rematch higher scoring networks first to prevent requests first matching a lower
-        // scoring network and then a higher scoring network, which could produce multiple
-        // callbacks.
-        Arrays.sort(nais);
-        final NetworkReassignment changes = computeInitialReassignment();
+    private void applyNetworkReassignment(@NonNull final NetworkReassignment changes,
+            final long now) {
+        final Collection<NetworkAgentInfo> nais = mNetworkAgentInfos.values();
+
+        // Since most of the time there are only 0 or 1 background networks, it would probably
+        // be more efficient to just use an ArrayList here. TODO : measure performance
+        final ArraySet<NetworkAgentInfo> oldBgNetworks = new ArraySet<>();
         for (final NetworkAgentInfo nai : nais) {
-            rematchNetworkAndRequests(changes, nai, now);
+            if (nai.isBackgroundNetwork()) oldBgNetworks.add(nai);
         }
 
+        // First, update the lists of satisfied requests in the network agents. This is necessary
+        // because some code later depends on this state to be correct, most prominently computing
+        // the linger status.
+        for (final NetworkReassignment.RequestReassignment event :
+                changes.getRequestReassignments()) {
+            updateSatisfiersForRematchRequest(event.mRequest, event.mOldNetwork,
+                    event.mNewNetwork, now);
+        }
+
+        final NetworkAgentInfo oldDefaultNetwork = getDefaultNetwork();
         final NetworkRequestInfo defaultRequestInfo = mNetworkRequests.get(mDefaultRequest);
         final NetworkReassignment.RequestReassignment reassignment =
                 changes.getReassignment(defaultRequestInfo);
@@ -6761,8 +6710,6 @@
         // before LegacyTypeTracker sends legacy broadcasts
         for (final NetworkReassignment.RequestReassignment event :
                 changes.getRequestReassignments()) {
-            if (event.mOldNetwork == event.mNewNetwork) continue;
-
             // Tell NetworkProviders about the new score, so they can stop
             // trying to connect if they know they cannot match it.
             // TODO - this could get expensive if there are a lot of outstanding requests for this
@@ -6777,17 +6724,10 @@
             }
         }
 
-        for (final NetworkReassignment.NetworkBgStatePair event : changes.getRematchedNetworks()) {
-            // Process listen requests and update capabilities if the background state has
-            // changed for this network. For consistency with previous behavior, send onLost
-            // callbacks before onAvailable.
-            processNewlyLostListenRequests(event.mNetwork);
-            if (event.mOldBackground != event.mNetwork.isBackgroundNetwork()) {
-                applyBackgroundChangeForRematch(event.mNetwork);
-            }
-            processNewlySatisfiedListenRequests(event.mNetwork);
-        }
-
+        // Update the linger state before processing listen callbacks, because the background
+        // computation depends on whether the network is lingering. Don't send the LOSING callbacks
+        // just yet though, because they have to be sent after the listens are processed to keep
+        // backward compatibility.
         final ArrayList<NetworkAgentInfo> lingeredNetworks = new ArrayList<>();
         for (final NetworkAgentInfo nai : nais) {
             // Rematching may have altered the linger state of some networks, so update all linger
@@ -6800,6 +6740,19 @@
             }
         }
 
+        for (final NetworkAgentInfo nai : nais) {
+            if (!nai.everConnected) continue;
+            final boolean oldBackground = oldBgNetworks.contains(nai);
+            // Process listen requests and update capabilities if the background state has
+            // changed for this network. For consistency with previous behavior, send onLost
+            // callbacks before onAvailable.
+            processNewlyLostListenRequests(nai);
+            if (oldBackground != nai.isBackgroundNetwork()) {
+                applyBackgroundChangeForRematch(nai);
+            }
+            processNewlySatisfiedListenRequests(nai);
+        }
+
         for (final NetworkAgentInfo nai : lingeredNetworks) {
             notifyNetworkLosing(nai, now);
         }
@@ -6821,7 +6774,7 @@
                         notifyNetworkLosing(nai, now);
                     }
                 } else {
-                    if (DBG) log("Reaping " + nai.name());
+                    if (DBG) log("Reaping " + nai.toShortString());
                     teardownUnneededNetwork(nai);
                 }
             }
@@ -6849,7 +6802,7 @@
     private void updateLegacyTypeTrackerAndVpnLockdownForRematch(
             @Nullable final NetworkAgentInfo oldDefaultNetwork,
             @Nullable final NetworkAgentInfo newDefaultNetwork,
-            @NonNull final NetworkAgentInfo[] nais) {
+            @NonNull final Collection<NetworkAgentInfo> nais) {
         if (oldDefaultNetwork != newDefaultNetwork) {
             // Maintain the illusion : since the legacy API only understands one network at a time,
             // if the default network changed, apps should see a disconnected broadcast for the
@@ -6888,7 +6841,9 @@
         // they may get old info. Reverse this after the old startUsing api is removed.
         // This is on top of the multiple intent sequencing referenced in the todo above.
         for (NetworkAgentInfo nai : nais) {
-            addNetworkToLegacyTypeTracker(nai);
+            if (nai.everConnected) {
+                addNetworkToLegacyTypeTracker(nai);
+            }
         }
     }
 
@@ -6978,8 +6933,8 @@
         notifyLockdownVpn(networkAgent);
 
         if (DBG) {
-            log(networkAgent.name() + " EVENT_NETWORK_INFO_CHANGED, going from " +
-                    oldInfo.getState() + " to " + state);
+            log(networkAgent.toShortString() + " EVENT_NETWORK_INFO_CHANGED, going from "
+                    + oldInfo.getState() + " to " + state);
         }
 
         if (!networkAgent.created
@@ -6997,7 +6952,7 @@
             networkAgent.everConnected = true;
 
             if (networkAgent.linkProperties == null) {
-                Slog.wtf(TAG, networkAgent.name() + " connected with null LinkProperties");
+                Slog.wtf(TAG, networkAgent.toShortString() + " connected with null LinkProperties");
             }
 
             // NetworkCapabilities need to be set before sending the private DNS config to
@@ -7057,7 +7012,7 @@
     }
 
     private void updateNetworkScore(NetworkAgentInfo nai, NetworkScore ns) {
-        if (VDBG || DDBG) log("updateNetworkScore for " + nai.name() + " to " + ns);
+        if (VDBG || DDBG) log("updateNetworkScore for " + nai.toShortString() + " to " + ns);
         nai.setNetworkScore(ns);
         rematchAllNetworksAndRequests();
         sendUpdatedScoreToFactories(nai);
@@ -7203,14 +7158,12 @@
     protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType, int arg1) {
         if (VDBG || DDBG) {
             String notification = ConnectivityManager.getCallbackName(notifyType);
-            log("notifyType " + notification + " for " + networkAgent.name());
+            log("notifyType " + notification + " for " + networkAgent.toShortString());
         }
         for (int i = 0; i < networkAgent.numNetworkRequests(); i++) {
             NetworkRequest nr = networkAgent.requestAt(i);
             NetworkRequestInfo nri = mNetworkRequests.get(nr);
             if (VDBG) log(" sending notification for " + nr);
-            // TODO: if we're in the middle of a rematch, can we send a CAP_CHANGED callback for
-            // a network that no longer satisfies the listen?
             if (nri.mPendingIntent == null) {
                 callCallbackForRequest(nri, networkAgent, notifyType, arg1);
             } else {
@@ -7830,7 +7783,13 @@
             @NonNull NetworkAgentInfo nai, long timestampMillis, int detectionMethod,
             @NonNull PersistableBundle extras) {
         final DataStallReport report =
-                new DataStallReport(nai.network, timestampMillis, detectionMethod, extras);
+                new DataStallReport(
+                        nai.network,
+                        timestampMillis,
+                        detectionMethod,
+                        nai.linkProperties,
+                        nai.networkCapabilities,
+                        extras);
         final List<IConnectivityDiagnosticsCallback> results =
                 getMatchingPermissionedCallbacks(nai);
         for (final IConnectivityDiagnosticsCallback cb : results) {
@@ -7907,12 +7866,13 @@
             throw new IllegalArgumentException("ConnectivityManager.TYPE_* are deprecated."
                     + " Please use NetworkCapabilities instead.");
         }
-        mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackageName);
+        final int callingUid = Binder.getCallingUid();
+        mAppOpsManager.checkPackage(callingUid, callingPackageName);
 
         // This NetworkCapabilities is only used for matching to Networks. Clear out its owner uid
         // and administrator uids to be safe.
         final NetworkCapabilities nc = new NetworkCapabilities(request.networkCapabilities);
-        restrictRequestUidsForCaller(nc);
+        restrictRequestUidsForCallerAndSetRequestorInfo(nc, callingUid, callingPackageName);
 
         final NetworkRequest requestWithId =
                 new NetworkRequest(
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index af8a366..5059a48 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -325,7 +325,7 @@
             mSlot = slot;
             int error = isValid();
             if (error == SUCCESS) {
-                Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.name());
+                Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.toShortString());
                 switch (mType) {
                     case TYPE_NATT:
                         mNai.asyncChannel.sendMessage(
@@ -365,7 +365,8 @@
                     Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
                 }
             }
-            Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.name() + ": " + reason);
+            Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.toShortString()
+                    + ": " + reason);
             switch (mStartedState) {
                 case NOT_STARTED:
                     // Remove the reference of the keepalive that meet error before starting,
@@ -476,7 +477,7 @@
     }
 
     public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) {
-        String networkName = (nai == null) ? "(null)" : nai.name();
+        final String networkName = NetworkAgentInfo.toShortString(nai);
         HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
         if (networkKeepalives == null) {
             Log.e(TAG, "Attempt to stop keepalive on nonexistent network " + networkName);
@@ -493,7 +494,7 @@
     }
 
     private void cleanupStoppedKeepalive(NetworkAgentInfo nai, int slot) {
-        String networkName = (nai == null) ? "(null)" : nai.name();
+        final String networkName = NetworkAgentInfo.toShortString(nai);
         HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
         if (networkKeepalives == null) {
             Log.e(TAG, "Attempt to remove keepalive on nonexistent network " + networkName);
@@ -540,7 +541,7 @@
         } catch(NullPointerException e) {}
         if (ki == null) {
             Log.e(TAG, "Event " + message.what + "," + slot + "," + reason
-                    + " for unknown keepalive " + slot + " on " + nai.name());
+                    + " for unknown keepalive " + slot + " on " + nai.toShortString());
             return;
         }
 
@@ -562,7 +563,7 @@
         if (KeepaliveInfo.STARTING == ki.mStartedState) {
             if (SUCCESS == reason) {
                 // Keepalive successfully started.
-                Log.d(TAG, "Started keepalive " + slot + " on " + nai.name());
+                Log.d(TAG, "Started keepalive " + slot + " on " + nai.toShortString());
                 ki.mStartedState = KeepaliveInfo.STARTED;
                 try {
                     ki.mCallback.onStarted(slot);
@@ -570,14 +571,14 @@
                     Log.w(TAG, "Discarded onStarted(" + slot + ") callback");
                 }
             } else {
-                Log.d(TAG, "Failed to start keepalive " + slot + " on " + nai.name()
+                Log.d(TAG, "Failed to start keepalive " + slot + " on " + nai.toShortString()
                         + ": " + reason);
                 // The message indicated some error trying to start: do call handleStopKeepalive.
                 handleStopKeepalive(nai, slot, reason);
             }
         } else if (KeepaliveInfo.STOPPING == ki.mStartedState) {
             // The message indicated result of stopping : clean up keepalive slots.
-            Log.d(TAG, "Stopped keepalive " + slot + " on " + nai.name()
+            Log.d(TAG, "Stopped keepalive " + slot + " on " + nai.toShortString()
                     + " stopped: " + reason);
             ki.mStartedState = KeepaliveInfo.NOT_STARTED;
             cleanupStoppedKeepalive(nai, slot);
@@ -733,7 +734,7 @@
         pw.println("Socket keepalives:");
         pw.increaseIndent();
         for (NetworkAgentInfo nai : mKeepalives.keySet()) {
-            pw.println(nai.name());
+            pw.println(nai.toShortString());
             pw.increaseIndent();
             for (int slot : mKeepalives.get(nai).keySet()) {
                 KeepaliveInfo ki = mKeepalives.get(nai).get(slot);
diff --git a/services/core/java/com/android/server/connectivity/LingerMonitor.java b/services/core/java/com/android/server/connectivity/LingerMonitor.java
index 7071510..04c000f 100644
--- a/services/core/java/com/android/server/connectivity/LingerMonitor.java
+++ b/services/core/java/com/android/server/connectivity/LingerMonitor.java
@@ -200,8 +200,9 @@
         }
 
         if (DBG) {
-            Log.d(TAG, "Notifying switch from=" + fromNai.name() + " to=" + toNai.name() +
-                    " type=" + sNotifyTypeNames.get(notifyType, "unknown(" + notifyType + ")"));
+            Log.d(TAG, "Notifying switch from=" + fromNai.toShortString()
+                    + " to=" + toNai.toShortString()
+                    + " type=" + sNotifyTypeNames.get(notifyType, "unknown(" + notifyType + ")"));
         }
 
         mNotifications.put(fromNai.network.netId, toNai.network.netId);
@@ -222,10 +223,10 @@
     public void noteLingerDefaultNetwork(@NonNull final NetworkAgentInfo fromNai,
             @Nullable final NetworkAgentInfo toNai) {
         if (VDBG) {
-            Log.d(TAG, "noteLingerDefaultNetwork from=" + fromNai.name() +
-                    " everValidated=" + fromNai.everValidated +
-                    " lastValidated=" + fromNai.lastValidated +
-                    " to=" + toNai.name());
+            Log.d(TAG, "noteLingerDefaultNetwork from=" + fromNai.toShortString()
+                    + " everValidated=" + fromNai.everValidated
+                    + " lastValidated=" + fromNai.lastValidated
+                    + " to=" + toNai.toShortString());
         }
 
         // If we are currently notifying the user because the device switched to fromNai, now that
@@ -270,7 +271,8 @@
         // TODO: should we do this?
         if (everNotified(fromNai)) {
             if (VDBG) {
-                Log.d(TAG, "Not notifying handover from " + fromNai.name() + ", already notified");
+                Log.d(TAG, "Not notifying handover from " + fromNai.toShortString()
+                        + ", already notified");
             }
             return;
         }
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index f636d67..82465f8 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -174,7 +174,7 @@
         try {
             mNMService.registerObserver(this);
         } catch (RemoteException e) {
-            Slog.e(TAG, "Can't register interface observer for clat on " + mNetwork.name());
+            Slog.e(TAG, "Can't register iface observer for clat on " + mNetwork.toShortString());
             return;
         }
 
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index d66aec5..d30a320 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -16,7 +16,10 @@
 
 package com.android.server.connectivity;
 
+import static android.net.NetworkCapabilities.transportNamesOf;
+
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.net.IDnsResolver;
 import android.net.INetd;
@@ -372,7 +375,7 @@
             // Should only happen if the requestId wraps. If that happens lots of other things will
             // be broken as well.
             Log.wtf(TAG, String.format("Duplicate requestId for %s and %s on %s",
-                    networkRequest, existing, name()));
+                    networkRequest, existing, toShortString()));
             updateRequestCounts(REMOVE, existing);
         }
         mNetworkRequests.put(networkRequest.requestId, networkRequest);
@@ -542,11 +545,11 @@
             // Cannot happen. Once a request is lingering on a particular network, we cannot
             // re-linger it unless that network becomes the best for that request again, in which
             // case we should have unlingered it.
-            Log.wtf(TAG, this.name() + ": request " + request.requestId + " already lingered");
+            Log.wtf(TAG, toShortString() + ": request " + request.requestId + " already lingered");
         }
         final long expiryMs = now + duration;
         LingerTimer timer = new LingerTimer(request, expiryMs);
-        if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + this.name());
+        if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + toShortString());
         mLingerTimers.add(timer);
         mLingerTimerForRequest.put(request.requestId, timer);
     }
@@ -558,7 +561,7 @@
     public boolean unlingerRequest(NetworkRequest request) {
         LingerTimer timer = mLingerTimerForRequest.get(request.requestId);
         if (timer != null) {
-            if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + this.name());
+            if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + toShortString());
             mLingerTimers.remove(timer);
             mLingerTimerForRequest.remove(request.requestId);
             return true;
@@ -645,9 +648,16 @@
                 + "}";
     }
 
-    public String name() {
-        return "NetworkAgentInfo [" + networkInfo.getTypeName() + " (" +
-                networkInfo.getSubtypeName() + ") - " + Objects.toString(network) + "]";
+    /**
+     * Show a short string representing a Network.
+     *
+     * This is often not enough for debugging purposes for anything complex, but the full form
+     * is very long and hard to read, so this is useful when there isn't a lot of ambiguity.
+     * This represents the network with something like "[100 WIFI|VPN]" or "[108 MOBILE]".
+     */
+    public String toShortString() {
+        return "[" + network.netId + " "
+                + transportNamesOf(networkCapabilities.getTransportTypes()) + "]";
     }
 
     // Enables sorting in descending order of score.
@@ -655,4 +665,12 @@
     public int compareTo(NetworkAgentInfo other) {
         return other.getCurrentScore() - getCurrentScore();
     }
+
+    /**
+     * Null-guarding version of NetworkAgentInfo#toShortString()
+     */
+    @NonNull
+    public static String toShortString(@Nullable final NetworkAgentInfo nai) {
+        return null != nai ? nai.toShortString() : "[null]";
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/NetworkRanker.java b/services/core/java/com/android/server/connectivity/NetworkRanker.java
new file mode 100644
index 0000000..d0aabf9
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/NetworkRanker.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.NetworkRequest;
+
+import java.util.Collection;
+
+/**
+ * A class that knows how to find the best network matching a request out of a list of networks.
+ */
+public class NetworkRanker {
+    public NetworkRanker() { }
+
+    /**
+     * Find the best network satisfying this request among the list of passed networks.
+     */
+    // Almost equivalent to Collections.max(nais), but allows returning null if no network
+    // satisfies the request.
+    @Nullable
+    public NetworkAgentInfo getBestNetwork(@NonNull final NetworkRequest request,
+            @NonNull final Collection<NetworkAgentInfo> nais) {
+        NetworkAgentInfo bestNetwork = null;
+        int bestScore = Integer.MIN_VALUE;
+        for (final NetworkAgentInfo nai : nais) {
+            if (!nai.satisfies(request)) continue;
+            if (nai.getCurrentScore() > bestScore) {
+                bestNetwork = nai;
+                bestScore = nai.getCurrentScore();
+            }
+        }
+        return bestNetwork;
+    }
+}
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 3e4f3d8..efea91a 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -272,10 +272,24 @@
         netCap.setOwnerUid(123);
         assertParcelingIsLossless(netCap);
         netCap.setSSID(TEST_SSID);
-        assertParcelSane(netCap, 13);
+        assertParcelSane(netCap, 15);
     }
 
     @Test
+    public void testParcelNetworkCapabilitiesWithRequestorUidAndPackageName() {
+        final NetworkCapabilities netCap = new NetworkCapabilities()
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .setRequestorUid(9304)
+                .setRequestorPackageName("com.android.test")
+                .addCapability(NET_CAPABILITY_EIMS)
+                .addCapability(NET_CAPABILITY_NOT_METERED);
+        assertParcelingIsLossless(netCap);
+        netCap.setSSID(TEST_SSID);
+        assertParcelSane(netCap, 15);
+    }
+
+
+    @Test
     public void testOemPaid() {
         NetworkCapabilities nc = new NetworkCapabilities();
         // By default OEM_PAID is neither in the unwanted or required lists and the network is not
diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
index 0628691..8eb5cfa 100644
--- a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
@@ -146,17 +146,14 @@
 
     @Test
     public void testConnectivityReportEquals() {
-        assertEquals(createSampleConnectivityReport(), createSampleConnectivityReport());
-        assertEquals(createDefaultConnectivityReport(), createDefaultConnectivityReport());
+        final ConnectivityReport defaultReport = createDefaultConnectivityReport();
+        final ConnectivityReport sampleReport = createSampleConnectivityReport();
+        assertEquals(sampleReport, createSampleConnectivityReport());
+        assertEquals(defaultReport, createDefaultConnectivityReport());
 
-        final LinkProperties linkProperties = new LinkProperties();
-        linkProperties.setInterfaceName(INTERFACE_NAME);
-
-        final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
-        networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
-
-        final PersistableBundle bundle = new PersistableBundle();
-        bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+        final LinkProperties linkProperties = sampleReport.getLinkProperties();
+        final NetworkCapabilities networkCapabilities = sampleReport.getNetworkCapabilities();
+        final PersistableBundle bundle = sampleReport.getAdditionalInfo();
 
         assertNotEquals(
                 createDefaultConnectivityReport(),
@@ -206,39 +203,104 @@
     }
 
     private DataStallReport createSampleDataStallReport() {
+        final LinkProperties linkProperties = new LinkProperties();
+        linkProperties.setInterfaceName(INTERFACE_NAME);
+
         final PersistableBundle bundle = new PersistableBundle();
         bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
-        return new DataStallReport(new Network(NET_ID), TIMESTAMP, DETECTION_METHOD, bundle);
+
+        final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+        networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+
+        return new DataStallReport(
+                new Network(NET_ID),
+                TIMESTAMP,
+                DETECTION_METHOD,
+                linkProperties,
+                networkCapabilities,
+                bundle);
     }
 
     private DataStallReport createDefaultDataStallReport() {
-        return new DataStallReport(new Network(0), 0L, 0, PersistableBundle.EMPTY);
+        return new DataStallReport(
+                new Network(0),
+                0L,
+                0,
+                new LinkProperties(),
+                new NetworkCapabilities(),
+                PersistableBundle.EMPTY);
     }
 
     @Test
     public void testDataStallReportEquals() {
-        assertEquals(createSampleDataStallReport(), createSampleDataStallReport());
-        assertEquals(createDefaultDataStallReport(), createDefaultDataStallReport());
+        final DataStallReport defaultReport = createDefaultDataStallReport();
+        final DataStallReport sampleReport = createSampleDataStallReport();
+        assertEquals(sampleReport, createSampleDataStallReport());
+        assertEquals(defaultReport, createDefaultDataStallReport());
 
-        final PersistableBundle bundle = new PersistableBundle();
-        bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+        final LinkProperties linkProperties = sampleReport.getLinkProperties();
+        final NetworkCapabilities networkCapabilities = sampleReport.getNetworkCapabilities();
+        final PersistableBundle bundle = sampleReport.getStallDetails();
 
         assertNotEquals(
-                createDefaultDataStallReport(),
-                new DataStallReport(new Network(NET_ID), 0L, 0, PersistableBundle.EMPTY));
+                defaultReport,
+                new DataStallReport(
+                        new Network(NET_ID),
+                        0L,
+                        0,
+                        new LinkProperties(),
+                        new NetworkCapabilities(),
+                        PersistableBundle.EMPTY));
         assertNotEquals(
-                createDefaultDataStallReport(),
-                new DataStallReport(new Network(0), TIMESTAMP, 0, PersistableBundle.EMPTY));
+                defaultReport,
+                new DataStallReport(
+                        new Network(0),
+                        TIMESTAMP,
+                        0,
+                        new LinkProperties(),
+                        new NetworkCapabilities(),
+                        PersistableBundle.EMPTY));
         assertNotEquals(
-                createDefaultDataStallReport(),
-                new DataStallReport(new Network(0), 0L, DETECTION_METHOD, PersistableBundle.EMPTY));
+                defaultReport,
+                new DataStallReport(
+                        new Network(0),
+                        0L,
+                        DETECTION_METHOD,
+                        new LinkProperties(),
+                        new NetworkCapabilities(),
+                        PersistableBundle.EMPTY));
         assertNotEquals(
-                createDefaultDataStallReport(), new DataStallReport(new Network(0), 0L, 0, bundle));
+                defaultReport,
+                new DataStallReport(
+                        new Network(0),
+                        0L,
+                        0,
+                        linkProperties,
+                        new NetworkCapabilities(),
+                        PersistableBundle.EMPTY));
+        assertNotEquals(
+                defaultReport,
+                new DataStallReport(
+                        new Network(0),
+                        0L,
+                        0,
+                        new LinkProperties(),
+                        networkCapabilities,
+                        PersistableBundle.EMPTY));
+        assertNotEquals(
+                defaultReport,
+                new DataStallReport(
+                        new Network(0),
+                        0L,
+                        0,
+                        new LinkProperties(),
+                        new NetworkCapabilities(),
+                        bundle));
     }
 
     @Test
     public void testDataStallReportParcelUnparcel() {
-        assertParcelSane(createSampleDataStallReport(), 4);
+        assertParcelSane(createSampleDataStallReport(), 6);
     }
 
     @Test
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index 7ede144..d6bf334 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -212,7 +212,8 @@
         ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class);
 
         // register callback
-        when(mService.requestNetwork(any(), captor.capture(), anyInt(), any(), anyInt()))
+        when(mService.requestNetwork(
+                any(), captor.capture(), anyInt(), any(), anyInt(), any()))
                 .thenReturn(request);
         manager.requestNetwork(request, callback, handler);
 
@@ -240,7 +241,8 @@
         ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class);
 
         // register callback
-        when(mService.requestNetwork(any(), captor.capture(), anyInt(), any(), anyInt()))
+        when(mService.requestNetwork(
+                any(), captor.capture(), anyInt(), any(), anyInt(), any()))
                 .thenReturn(req1);
         manager.requestNetwork(req1, callback, handler);
 
@@ -258,7 +260,8 @@
         verify(callback, timeout(100).times(0)).onLosing(any(), anyInt());
 
         // callback can be registered again
-        when(mService.requestNetwork(any(), captor.capture(), anyInt(), any(), anyInt()))
+        when(mService.requestNetwork(
+                any(), captor.capture(), anyInt(), any(), anyInt(), any()))
                 .thenReturn(req2);
         manager.requestNetwork(req2, callback, handler);
 
@@ -282,7 +285,8 @@
         info.targetSdkVersion = VERSION_CODES.N_MR1 + 1;
 
         when(mCtx.getApplicationInfo()).thenReturn(info);
-        when(mService.requestNetwork(any(), any(), anyInt(), any(), anyInt())).thenReturn(request);
+        when(mService.requestNetwork(any(), any(), anyInt(), any(), anyInt(), any()))
+                .thenReturn(request);
 
         Handler handler = new Handler(Looper.getMainLooper());
         manager.requestNetwork(request, callback, handler);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 4373af9..b2464ba 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -107,6 +107,7 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -305,6 +306,7 @@
     private static final String MOBILE_IFNAME = "test_rmnet_data0";
     private static final String WIFI_IFNAME = "test_wlan0";
     private static final String WIFI_WOL_IFNAME = "test_wlan_wol";
+    private static final String TEST_PACKAGE_NAME = "com.android.test.package";
     private static final String[] EMPTY_STRING_ARRAY = new String[0];
 
     private MockContext mServiceContext;
@@ -654,7 +656,7 @@
 
             if (mNmValidationRedirectUrl != null) {
                 mNmCallbacks.showProvisioningNotification(
-                        "test_provisioning_notif_action", "com.android.test.package");
+                        "test_provisioning_notif_action", TEST_PACKAGE_NAME);
                 mNmProvNotificationRequested = true;
             }
         }
@@ -2972,7 +2974,7 @@
             networkCapabilities.addTransportType(TRANSPORT_WIFI)
                     .setNetworkSpecifier(new MatchAllNetworkSpecifier());
             mService.requestNetwork(networkCapabilities, null, 0, null,
-                    ConnectivityManager.TYPE_WIFI);
+                    ConnectivityManager.TYPE_WIFI, TEST_PACKAGE_NAME);
         });
 
         class NonParcelableSpecifier extends NetworkSpecifier {
@@ -3011,31 +3013,12 @@
     }
 
     @Test
-    public void testNetworkSpecifierUidSpoofSecurityException() throws Exception {
-        class UidAwareNetworkSpecifier extends NetworkSpecifier implements Parcelable {
-            @Override
-            public boolean satisfiedBy(NetworkSpecifier other) {
-                return true;
-            }
-
-            @Override
-            public void assertValidFromUid(int requestorUid) {
-                throw new SecurityException("failure");
-            }
-
-            @Override
-            public int describeContents() { return 0; }
-            @Override
-            public void writeToParcel(Parcel dest, int flags) {}
-        }
-
+    public void testNetworkRequestUidSpoofSecurityException() throws Exception {
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
-
-        UidAwareNetworkSpecifier networkSpecifier = new UidAwareNetworkSpecifier();
-        NetworkRequest networkRequest = newWifiRequestBuilder().setNetworkSpecifier(
-                networkSpecifier).build();
+        NetworkRequest networkRequest = newWifiRequestBuilder().build();
         TestNetworkCallback networkCallback = new TestNetworkCallback();
+        doThrow(new SecurityException()).when(mAppOpsManager).checkPackage(anyInt(), anyString());
         assertThrows(SecurityException.class, () -> {
             mCm.requestNetwork(networkRequest, networkCallback);
         });
diff --git a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
new file mode 100644
index 0000000..86c9116
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity
+
+import android.net.NetworkRequest
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import kotlin.test.assertEquals
+import kotlin.test.assertNull
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class NetworkRankerTest {
+    private val ranker = NetworkRanker()
+
+    private fun makeNai(satisfy: Boolean, score: Int) = mock(NetworkAgentInfo::class.java).also {
+        doReturn(satisfy).`when`(it).satisfies(any())
+        doReturn(score).`when`(it).currentScore
+    }
+
+    @Test
+    fun testGetBestNetwork() {
+        val scores = listOf(20, 50, 90, 60, 23, 68)
+        val nais = scores.map { makeNai(true, it) }
+        val bestNetwork = nais[2] // The one with the top score
+        val someRequest = mock(NetworkRequest::class.java)
+        assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais))
+    }
+
+    @Test
+    fun testIgnoreNonSatisfying() {
+        val nais = listOf(makeNai(true, 20), makeNai(true, 50), makeNai(false, 90),
+                makeNai(false, 60), makeNai(true, 23), makeNai(false, 68))
+        val bestNetwork = nais[1] // Top score that's satisfying
+        val someRequest = mock(NetworkRequest::class.java)
+        assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais))
+    }
+
+    @Test
+    fun testNoMatch() {
+        val nais = listOf(makeNai(false, 20), makeNai(false, 50), makeNai(false, 90))
+        val someRequest = mock(NetworkRequest::class.java)
+        assertNull(ranker.getBestNetwork(someRequest, nais))
+    }
+
+    @Test
+    fun testEmpty() {
+        val someRequest = mock(NetworkRequest::class.java)
+        assertNull(ranker.getBestNetwork(someRequest, emptyList()))
+    }
+
+    // Make sure the ranker is "stable" (as in stable sort), that is, it always returns the FIRST
+    // network satisfying the request if multiple of them have the same score.
+    @Test
+    fun testStable() {
+        val nais1 = listOf(makeNai(true, 30), makeNai(true, 30), makeNai(true, 30),
+                makeNai(true, 30), makeNai(true, 30), makeNai(true, 30))
+        val someRequest = mock(NetworkRequest::class.java)
+        assertEquals(nais1[0], ranker.getBestNetwork(someRequest, nais1))
+
+        val nais2 = listOf(makeNai(true, 30), makeNai(true, 50), makeNai(true, 20),
+                makeNai(true, 50), makeNai(true, 50), makeNai(true, 40))
+        assertEquals(nais2[1], ranker.getBestNetwork(someRequest, nais2))
+    }
+}
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 155c61f..eb78529 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -148,6 +148,7 @@
     @Mock private AppOpsManager mAppOps;
     @Mock private NotificationManager mNotificationManager;
     @Mock private Vpn.SystemServices mSystemServices;
+    @Mock private Vpn.Ikev2SessionCreator mIkev2SessionCreator;
     @Mock private ConnectivityManager mConnectivityManager;
     @Mock private KeyStore mKeyStore;
     private final VpnProfile mVpnProfile = new VpnProfile("key");
@@ -867,7 +868,8 @@
      * Mock some methods of vpn object.
      */
     private Vpn createVpn(@UserIdInt int userId) {
-        return new Vpn(Looper.myLooper(), mContext, mNetService, userId, mSystemServices);
+        return new Vpn(Looper.myLooper(), mContext, mNetService,
+                userId, mSystemServices, mIkev2SessionCreator);
     }
 
     private static void assertBlocked(Vpn vpn, int... uids) {