Merge "Add core notifications team as owners of internal notification widgets."
diff --git a/core/api/current.txt b/core/api/current.txt
index 285506f..f453cca 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1142,6 +1142,7 @@
     field public static final int reqNavigation = 16843306; // 0x101022a
     field public static final int reqTouchScreen = 16843303; // 0x1010227
     field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603
+    field public static final int requireDeviceScreenOn = 16844312; // 0x1010618
     field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
     field public static final int required = 16843406; // 0x101028e
     field public static final int requiredAccountType = 16843734; // 0x10103d6
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 854e8fd..ab9799f 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -61,6 +61,16 @@
     method public void teardownTestNetwork(@NonNull android.net.Network);
   }
 
+  public final class UnderlyingNetworkInfo implements android.os.Parcelable {
+    ctor public UnderlyingNetworkInfo(int, @NonNull String, @NonNull java.util.List<java.lang.String>);
+    method public int describeContents();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.UnderlyingNetworkInfo> CREATOR;
+    field @NonNull public final String iface;
+    field public final int ownerUid;
+    field @NonNull public final java.util.List<java.lang.String> underlyingIfaces;
+  }
+
 }
 
 package android.os {
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 1ddfe0d..1d5dc1d 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -28,7 +28,6 @@
 import android.net.ConnectivityManager;
 import android.net.DataUsageRequest;
 import android.net.INetworkStatsService;
-import android.net.NetworkIdentity;
 import android.net.NetworkStack;
 import android.net.NetworkTemplate;
 import android.net.netstats.provider.INetworkStatsProviderCallback;
@@ -47,6 +46,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.NetworkIdentityUtils;
 
 import java.util.Objects;
 
@@ -628,7 +628,7 @@
             default:
                 throw new IllegalArgumentException("Cannot create template for network type "
                         + networkType + ", subscriberId '"
-                        + NetworkIdentity.scrubSubscriberId(subscriberId) + "'.");
+                        + NetworkIdentityUtils.scrubSubscriberId(subscriberId) + "'.");
         }
         return template;
     }
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index b644ed5..521ed9b 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -21,11 +21,12 @@
 import android.content.Context;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
-import android.os.Build;
 import android.service.NetworkIdentityProto;
 import android.telephony.Annotation.NetworkType;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.net.module.util.NetworkIdentityUtils;
+
 import java.util.Objects;
 
 /**
@@ -89,7 +90,8 @@
             builder.append(mSubType);
         }
         if (mSubscriberId != null) {
-            builder.append(", subscriberId=").append(scrubSubscriberId(mSubscriberId));
+            builder.append(", subscriberId=")
+                    .append(NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
         }
         if (mNetworkId != null) {
             builder.append(", networkId=").append(mNetworkId);
@@ -110,7 +112,8 @@
         // Not dumping mSubType, subtypes are no longer supported.
 
         if (mSubscriberId != null) {
-            proto.write(NetworkIdentityProto.SUBSCRIBER_ID, scrubSubscriberId(mSubscriberId));
+            proto.write(NetworkIdentityProto.SUBSCRIBER_ID,
+                    NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
         }
         proto.write(NetworkIdentityProto.NETWORK_ID, mNetworkId);
         proto.write(NetworkIdentityProto.ROAMING, mRoaming);
@@ -149,32 +152,6 @@
     }
 
     /**
-     * Scrub given IMSI on production builds.
-     */
-    public static String scrubSubscriberId(String subscriberId) {
-        if (Build.IS_ENG) {
-            return subscriberId;
-        } else if (subscriberId != null) {
-            // TODO: parse this as MCC+MNC instead of hard-coding
-            return subscriberId.substring(0, Math.min(6, subscriberId.length())) + "...";
-        } else {
-            return "null";
-        }
-    }
-
-    /**
-     * Scrub given IMSI on production builds.
-     */
-    public static String[] scrubSubscriberId(String[] subscriberId) {
-        if (subscriberId == null) return null;
-        final String[] res = new String[subscriberId.length];
-        for (int i = 0; i < res.length; i++) {
-            res[i] = NetworkIdentity.scrubSubscriberId(subscriberId[i]);
-        }
-        return res;
-    }
-
-    /**
      * Build a {@link NetworkIdentity} from the given {@link NetworkState} and {@code subType},
      * assuming that any mobile networks are using the current IMSI. The subType if applicable,
      * should be set as one of the TelephonyManager.NETWORK_TYPE_* constants, or
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 72be835..2322048 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -48,6 +48,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
+import com.android.net.module.util.NetworkIdentityUtils;
 
 import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
@@ -296,11 +297,11 @@
         builder.append("matchRule=").append(getMatchRuleName(mMatchRule));
         if (mSubscriberId != null) {
             builder.append(", subscriberId=").append(
-                    NetworkIdentity.scrubSubscriberId(mSubscriberId));
+                    NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
         }
         if (mMatchSubscriberIds != null) {
             builder.append(", matchSubscriberIds=").append(
-                    Arrays.toString(NetworkIdentity.scrubSubscriberId(mMatchSubscriberIds)));
+                    Arrays.toString(NetworkIdentityUtils.scrubSubscriberIds(mMatchSubscriberIds)));
         }
         if (mNetworkId != null) {
             builder.append(", networkId=").append(mNetworkId);
diff --git a/core/java/android/net/UnderlyingNetworkInfo.java b/core/java/android/net/UnderlyingNetworkInfo.java
index 8fb4832..7bf9231 100644
--- a/core/java/android/net/UnderlyingNetworkInfo.java
+++ b/core/java/android/net/UnderlyingNetworkInfo.java
@@ -16,11 +16,15 @@
 
 package android.net;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
@@ -30,6 +34,7 @@
  *
  * @hide
  */
+@SystemApi(client = MODULE_LIBRARIES)
 public final class UnderlyingNetworkInfo implements Parcelable {
     /** The owner of this network. */
     public final int ownerUid;
@@ -46,7 +51,7 @@
         Objects.requireNonNull(underlyingIfaces);
         this.ownerUid = ownerUid;
         this.iface = iface;
-        this.underlyingIfaces = underlyingIfaces;
+        this.underlyingIfaces = Collections.unmodifiableList(new ArrayList<>(underlyingIfaces));
     }
 
     private UnderlyingNetworkInfo(@NonNull Parcel in) {
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index d7c2e05..64ab074 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -97,6 +97,11 @@
     final boolean mRequiresDeviceUnlock;
 
     /**
+     * Whether this service should only be started when the device is screen on.
+     */
+    final boolean mRequiresDeviceScreenOn;
+
+    /**
      * The id of the service banner specified in XML.
      */
     final int mBannerResourceId;
@@ -119,6 +124,18 @@
             ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups,
             boolean requiresUnlock, int bannerResource, int uid,
             String settingsActivityName, String offHost, String staticOffHost) {
+        this(info, onHost, description, staticAidGroups, dynamicAidGroups,
+                requiresUnlock, onHost ? true : false, bannerResource, uid,
+                settingsActivityName, offHost, staticOffHost);
+    }
+
+    /**
+     * @hide
+     */
+    public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
+            ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups,
+            boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid,
+            String settingsActivityName, String offHost, String staticOffHost) {
         this.mService = info;
         this.mDescription = description;
         this.mStaticAidGroups = new HashMap<String, AidGroup>();
@@ -127,6 +144,7 @@
         this.mStaticOffHostName = staticOffHost;
         this.mOnHost = onHost;
         this.mRequiresDeviceUnlock = requiresUnlock;
+        this.mRequiresDeviceScreenOn = requiresScreenOn;
         for (AidGroup aidGroup : staticAidGroups) {
             this.mStaticAidGroups.put(aidGroup.category, aidGroup);
         }
@@ -183,6 +201,9 @@
                 mRequiresDeviceUnlock = sa.getBoolean(
                         com.android.internal.R.styleable.HostApduService_requireDeviceUnlock,
                         false);
+                mRequiresDeviceScreenOn = sa.getBoolean(
+                        com.android.internal.R.styleable.HostApduService_requireDeviceScreenOn,
+                        true);
                 mBannerResourceId = sa.getResourceId(
                         com.android.internal.R.styleable.HostApduService_apduServiceBanner, -1);
                 mSettingsActivityName = sa.getString(
@@ -196,7 +217,12 @@
                 mService = info;
                 mDescription = sa.getString(
                         com.android.internal.R.styleable.OffHostApduService_description);
-                mRequiresDeviceUnlock = false;
+                mRequiresDeviceUnlock = sa.getBoolean(
+                        com.android.internal.R.styleable.OffHostApduService_requireDeviceUnlock,
+                        false);
+                mRequiresDeviceScreenOn = sa.getBoolean(
+                        com.android.internal.R.styleable.OffHostApduService_requireDeviceScreenOn,
+                        false);
                 mBannerResourceId = sa.getResourceId(
                         com.android.internal.R.styleable.OffHostApduService_apduServiceBanner, -1);
                 mSettingsActivityName = sa.getString(
@@ -419,6 +445,13 @@
         return mRequiresDeviceUnlock;
     }
 
+    /**
+     * Returns whether this service should only be started when the device is screen on.
+     */
+    public boolean requiresScreenOn() {
+        return mRequiresDeviceScreenOn;
+    }
+
     @UnsupportedAppUsage
     public String getDescription() {
         return mDescription;
@@ -542,6 +575,7 @@
             dest.writeTypedList(new ArrayList<AidGroup>(mDynamicAidGroups.values()));
         }
         dest.writeInt(mRequiresDeviceUnlock ? 1 : 0);
+        dest.writeInt(mRequiresDeviceScreenOn ? 1 : 0);
         dest.writeInt(mBannerResourceId);
         dest.writeInt(mUid);
         dest.writeString(mSettingsActivityName);
@@ -568,11 +602,12 @@
                 source.readTypedList(dynamicAidGroups, AidGroup.CREATOR);
             }
             boolean requiresUnlock = source.readInt() != 0;
+            boolean requiresScreenOn = source.readInt() != 0;
             int bannerResource = source.readInt();
             int uid = source.readInt();
             String settingsActivityName = source.readString();
             return new ApduServiceInfo(info, onHost, description, staticAidGroups,
-                    dynamicAidGroups, requiresUnlock, bannerResource, uid,
+                    dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid,
                     settingsActivityName, offHostName, staticOffHostName);
         }
 
@@ -607,6 +642,8 @@
             }
         }
         pw.println("    Settings Activity: " + mSettingsActivityName);
+        pw.println("    Requires Device Unlock: " + mRequiresDeviceUnlock);
+        pw.println("    Requires Device ScreenOn: " + mRequiresDeviceScreenOn);
     }
 
     /**
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a668e8e..0e80c7b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1673,7 +1673,7 @@
      @SystemApi
      <p>Not for use by third-party applications. @hide -->
     <permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM"
-                android:protectionLevel="signature|setup" />
+                android:protectionLevel="signature|privileged" />
 
     <!-- @SystemApi @hide Allows applications to toggle airplane mode.
          <p>Not for use by third-party or privileged applications.
diff --git a/core/res/OWNERS b/core/res/OWNERS
index a30111b..9d739b9 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -6,9 +6,12 @@
 dupin@google.com
 hackbod@android.com
 hackbod@google.com
+ilyamaty@google.com
+jaggies@google.com
 jsharkey@android.com
 jsharkey@google.com
 juliacr@google.com
+kchyn@google.com
 michaelwr@google.com
 nandana@google.com
 narayan@google.com
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 4b3d82a..9cc0690 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3856,6 +3856,9 @@
         <!-- Component name of an activity that allows the user to modify
              the settings for this service. -->
         <attr name="settingsActivity"/>
+        <!-- Whether the device must be screen on before routing data to this service.
+             The default is true.-->
+        <attr name="requireDeviceScreenOn" format="boolean"/>
     </declare-styleable>
 
     <!-- Use <code>offhost-apdu-service</code> as the root tag of the XML resource that
@@ -3874,6 +3877,12 @@
         <attr name="settingsActivity"/>
         <!-- Secure Element which the AIDs should be routed to -->
         <attr name="secureElementName" format="string"/>
+        <!-- Whether the device must be unlocked before routing data to this service.
+             The default is false.-->
+        <attr name="requireDeviceUnlock"/>
+        <!-- Whether the device must be screen on before routing data to this service.
+             The default is false.-->
+        <attr name="requireDeviceScreenOn"/>
     </declare-styleable>
 
     <!-- Specify one or more <code>aid-group</code> elements inside a
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a0be068..0874a77 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3045,6 +3045,7 @@
   <public-group type="attr" first-id="0x01010617">
     <public name="canPauseRecording" />
     <!-- attribute definitions go here -->
+    <public name="requireDeviceScreenOn" />
   </public-group>
 
   <public-group type="drawable" first-id="0x010800b5">
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index c091dfa..f2e1920 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -739,11 +739,11 @@
         }
 
         private void maybeLogBroadcast(NetworkAgentInfo nai, DetailedState state, int type,
-                boolean isDefaultNetwork) {
+                boolean isFallbackNetwork) {
             if (DBG) {
                 log("Sending " + state
                         + " broadcast for type " + type + " " + nai.toShortString()
-                        + " isDefaultNetwork=" + isDefaultNetwork);
+                        + " isFallbackNetwork=" + isFallbackNetwork);
             }
         }
 
@@ -762,10 +762,10 @@
                 list.add(nai);
             }
 
-            // Send a broadcast if this is the first network of its type or if it's the default.
-            final boolean isDefaultNetwork = mService.isDefaultNetwork(nai);
-            if ((list.size() == 1) || isDefaultNetwork) {
-                maybeLogBroadcast(nai, DetailedState.CONNECTED, type, isDefaultNetwork);
+            // Send a broadcast if this is the first network of its type or if it's the fallback.
+            final boolean isFallbackNetwork = mService.isFallbackNetwork(nai);
+            if ((list.size() == 1) || isFallbackNetwork) {
+                maybeLogBroadcast(nai, DetailedState.CONNECTED, type, isFallbackNetwork);
                 mService.sendLegacyNetworkBroadcast(nai, DetailedState.CONNECTED, type);
             }
         }
@@ -794,7 +794,7 @@
                               ", sending connected broadcast");
                 final NetworkAgentInfo replacement = list.get(0);
                 maybeLogBroadcast(replacement, DetailedState.CONNECTED, type,
-                        mService.isDefaultNetwork(replacement));
+                        mService.isFallbackNetwork(replacement));
                 mService.sendLegacyNetworkBroadcast(replacement, DetailedState.CONNECTED, type);
             }
         }
@@ -810,14 +810,14 @@
         // send out another legacy broadcast - currently only used for suspend/unsuspend
         // toggle
         public void update(NetworkAgentInfo nai) {
-            final boolean isDefault = mService.isDefaultNetwork(nai);
+            final boolean isFallback = mService.isFallbackNetwork(nai);
             final DetailedState state = nai.networkInfo.getDetailedState();
             for (int type = 0; type < mTypeLists.length; type++) {
                 final ArrayList<NetworkAgentInfo> list = mTypeLists[type];
                 final boolean contains = (list != null && list.contains(nai));
                 final boolean isFirst = contains && (nai == list.get(0));
-                if (isFirst || contains && isDefault) {
-                    maybeLogBroadcast(nai, state, type, isDefault);
+                if (isFirst || contains && isFallback) {
+                    maybeLogBroadcast(nai, state, type, isFallback);
                     mService.sendLegacyNetworkBroadcast(nai, state, type);
                 }
             }
@@ -1021,11 +1021,13 @@
         mNetworkRequestCounter = new PerUidCounter(MAX_NETWORK_REQUESTS_PER_UID);
 
         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);
+        final NetworkRequest fallbackRequest = createDefaultInternetRequestForTransport(
+                -1, NetworkRequest.Type.REQUEST);
+        mFallbackRequest = new NetworkRequestInfo(null, fallbackRequest, new Binder());
+        mNetworkRequests.put(fallbackRequest, mFallbackRequest);
+        mDefaultNetworkRequests.add(mFallbackRequest);
+        mNetworkRequestInfoLogs.log("REGISTER " + mFallbackRequest);
 
         mDefaultMobileDataRequest = createDefaultInternetRequestForTransport(
                 NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST);
@@ -1364,7 +1366,7 @@
     }
 
     private NetworkState getUnfilteredActiveNetworkState(int uid) {
-        NetworkAgentInfo nai = getDefaultNetwork();
+        NetworkAgentInfo nai = getFallbackNetwork();
 
         final Network[] networks = getVpnUnderlyingNetworks(uid);
         if (networks != null) {
@@ -1497,7 +1499,7 @@
             }
         }
 
-        NetworkAgentInfo nai = getDefaultNetwork();
+        NetworkAgentInfo nai = getFallbackNetwork();
         if (nai == null || isNetworkWithCapabilitiesBlocked(nai.networkCapabilities, uid,
                 ignoreBlocked)) {
             return null;
@@ -1636,7 +1638,7 @@
 
         HashMap<Network, NetworkCapabilities> result = new HashMap<>();
 
-        NetworkAgentInfo nai = getDefaultNetwork();
+        final NetworkAgentInfo nai = getFallbackNetwork();
         NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
         if (nc != null) {
             result.put(
@@ -2023,7 +2025,7 @@
             // TODO: Move the Dns Event to NetworkMonitor. NetdEventListenerService only allow one
             // callback from each caller type. Need to re-factor NetdEventListenerService to allow
             // multiple NetworkMonitor registrants.
-            if (nai != null && nai.satisfies(mDefaultRequest)) {
+            if (nai != null && nai.satisfies(mFallbackRequest.mRequests.get(0))) {
                 nai.networkMonitor().notifyDnsResponse(returnCode);
             }
         }
@@ -2580,12 +2582,12 @@
         pw.println();
         pw.println();
 
-        final NetworkAgentInfo defaultNai = getDefaultNetwork();
+        final NetworkAgentInfo fallbackNai = getFallbackNetwork();
         pw.print("Active default network: ");
-        if (defaultNai == null) {
+        if (fallbackNai == null) {
             pw.println("none");
         } else {
-            pw.println(defaultNai.network.getNetId());
+            pw.println(fallbackNai.network.getNetId());
         }
         pw.println();
 
@@ -2968,7 +2970,7 @@
 
             final boolean valid = ((testResult & NETWORK_VALIDATION_RESULT_VALID) != 0);
             final boolean wasValidated = nai.lastValidated;
-            final boolean wasDefault = isDefaultNetwork(nai);
+            final boolean wasFallback = isFallbackNetwork(nai);
 
             if (DBG) {
                 final String logMsg = !TextUtils.isEmpty(redirectUrl)
@@ -2977,7 +2979,7 @@
                 log(nai.toShortString() + " validation " + (valid ? "passed" : "failed") + logMsg);
             }
             if (valid != nai.lastValidated) {
-                if (wasDefault) {
+                if (wasFallback) {
                     mMetricsLog.logDefaultNetworkValidity(valid);
                 }
                 final int oldScore = nai.getCurrentScore();
@@ -3353,13 +3355,13 @@
             loge("Error connecting NetworkAgent");
             mNetworkAgentInfos.remove(nai);
             if (nai != null) {
-                final boolean wasDefault = isDefaultNetwork(nai);
+                final boolean wasFallback = isFallbackNetwork(nai);
                 synchronized (mNetworkForNetId) {
                     mNetworkForNetId.remove(nai.network.getNetId());
                 }
                 mNetIdManager.releaseNetId(nai.network.getNetId());
                 // Just in case.
-                mLegacyTypeTracker.remove(nai, wasDefault);
+                mLegacyTypeTracker.remove(nai, wasFallback);
             }
         }
     }
@@ -3398,8 +3400,8 @@
             nai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
                     null, null);
         }
-        final boolean wasDefault = isDefaultNetwork(nai);
-        if (wasDefault) {
+        final boolean wasFallback = isFallbackNetwork(nai);
+        if (wasFallback) {
             mDefaultInetConditionPublished = 0;
             // Log default network disconnection before required book-keeping.
             // Let rematchAllNetworksAndRequests() below record a new default network event
@@ -3441,19 +3443,24 @@
                     && currentNetwork.network.getNetId() == nai.network.getNetId()) {
                 nri.setSatisfier(null, null);
                 sendUpdatedScoreToFactories(request, null);
+
+                if (mFallbackRequest == nri) {
+                    // TODO : make battery stats aware that since 2013 multiple interfaces may be
+                    //  active at the same time. For now keep calling this with the fallback
+                    //  network, because while incorrect this is the closest to the old (also
+                    //  incorrect) behavior.
+                    mNetworkActivityTracker.updateDataActivityTracking(
+                            null /* newNetwork */, nai);
+                    notifyLockdownVpn(nai);
+                    ensureNetworkTransitionWakelock(nai.toShortString());
+                }
             }
         }
         nai.clearLingerState();
-        // TODO: this loop, and the mLegacyTypeTracker.remove just below it, seem redundant given
-        // there's a full rematch right after. Currently, deleting it breaks tests that check for
-        // the default network disconnecting. Find out why, fix the rematch code, and delete this.
-        if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
-            mDefaultNetworkNai = null;
-            mNetworkActivityTracker.updateDataActivityTracking(null /* newNetwork */, nai);
-            notifyLockdownVpn(nai);
-            ensureNetworkTransitionWakelock(nai.toShortString());
-        }
-        mLegacyTypeTracker.remove(nai, wasDefault);
+        // TODO: mLegacyTypeTracker.remove seems redundant given there's a full rematch right after.
+        //  Currently, deleting it breaks tests that check for the fallback network disconnecting.
+        //  Find out why, fix the rematch code, and delete this.
+        mLegacyTypeTracker.remove(nai, wasFallback);
         rematchAllNetworksAndRequests();
         mLingerMonitor.noteDisconnect(nai);
         if (nai.created) {
@@ -4253,7 +4260,7 @@
 
     @Override
     public NetworkRequest getDefaultRequest() {
-        return mDefaultRequest;
+        return mFallbackRequest.mRequests.get(0);
     }
 
     private class InternalHandler extends Handler {
@@ -4499,7 +4506,7 @@
         // revalidate the network and generate a ConnectivityDiagnostics ConnectivityReport event.
         final NetworkAgentInfo nai;
         if (network == null) {
-            nai = getDefaultNetwork();
+            nai = getFallbackNetwork();
         } else {
             nai = getNetworkAgentInfoForNetwork(network);
         }
@@ -4518,7 +4525,7 @@
             Network network, int uid, boolean hasConnectivity) {
         final NetworkAgentInfo nai;
         if (network == null) {
-            nai = getDefaultNetwork();
+            nai = getFallbackNetwork();
         } else {
             nai = getNetworkAgentInfoForNetwork(network);
         }
@@ -4831,7 +4838,7 @@
         }
         synchronized (mVpns) {
             throwIfLockdownEnabled();
-            mVpns.get(user).startLegacyVpn(profile, mKeyStore, egress);
+            mVpns.get(user).startLegacyVpn(profile, mKeyStore, null /* underlying */, egress);
         }
     }
 
@@ -4884,7 +4891,7 @@
         // see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret
         // the underlyingNetworks list.
         if (underlyingNetworks == null) {
-            NetworkAgentInfo defaultNai = getDefaultNetwork();
+            final NetworkAgentInfo defaultNai = getFallbackNetwork();
             if (defaultNai != null) {
                 underlyingNetworks = new Network[] { defaultNai.network };
             }
@@ -4936,7 +4943,7 @@
     }
 
     private Network[] underlyingNetworksOrDefault(Network[] underlyingNetworks) {
-        final Network defaultNetwork = getNetwork(getDefaultNetwork());
+        final Network defaultNetwork = getNetwork(getFallbackNetwork());
         if (underlyingNetworks == null && defaultNetwork != null) {
             // null underlying networks means to track the default.
             underlyingNetworks = new Network[] { defaultNetwork };
@@ -5493,6 +5500,8 @@
      */
     @VisibleForTesting
     protected class NetworkRequestInfo implements IBinder.DeathRecipient {
+        // The requests to be satisfied in priority order. Non-multilayer requests will only have a
+        // single NetworkRequest in mRequests.
         final List<NetworkRequest> mRequests;
 
         // mSatisfier and mActiveRequest rely on one another therefore set them together.
@@ -6038,11 +6047,13 @@
     @GuardedBy("mBlockedAppUids")
     private final HashSet<Integer> mBlockedAppUids = new HashSet<>();
 
+    // The always-on request for an Internet-capable network that apps without a specific default
+    // fall back to.
     @NonNull
-    private final NetworkRequest mDefaultRequest;
-    // The NetworkAgentInfo currently satisfying the default request, if any.
-    @Nullable
-    private volatile NetworkAgentInfo mDefaultNetworkNai = null;
+    private final NetworkRequestInfo mFallbackRequest;
+    // Collection of NetworkRequestInfo's used for default networks.
+    @NonNull
+    private final ArraySet<NetworkRequestInfo> mDefaultNetworkRequests = new ArraySet<>();
 
     // Request used to optionally keep mobile data active even when higher
     // priority networks like Wi-Fi are active.
@@ -6055,8 +6066,10 @@
     // Request used to optionally keep vehicle internal network always active
     private final NetworkRequest mDefaultVehicleRequest;
 
-    private NetworkAgentInfo getDefaultNetwork() {
-        return mDefaultNetworkNai;
+    // TODO: b/178729499 update this in favor of a method taking in a UID.
+    // The NetworkAgentInfo currently satisfying the fallback request, if any.
+    private NetworkAgentInfo getFallbackNetwork() {
+        return mFallbackRequest.mSatisfier;
     }
 
     @Nullable
@@ -6073,8 +6086,8 @@
     }
 
     @VisibleForTesting
-    protected boolean isDefaultNetwork(NetworkAgentInfo nai) {
-        return nai == getDefaultNetwork();
+    protected boolean isFallbackNetwork(NetworkAgentInfo nai) {
+        return nai == getFallbackNetwork();
     }
 
     // TODO : remove this method. It's a stopgap measure to help sheperding a number of dependent
@@ -6143,8 +6156,8 @@
 
         LinkProperties lp = new LinkProperties(linkProperties);
 
-        // TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
-        // satisfies mDefaultRequest.
+        // TODO: Instead of passing mFallbackRequest, provide an API to determine whether a Network
+        // satisfies mFallbackRequest.
         final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
         final NetworkAgentInfo nai = new NetworkAgentInfo(na,
                 new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
@@ -6221,7 +6234,7 @@
 //        for (LinkProperties lp : newLp.getStackedLinks()) {
 //            updateMtu(lp, null);
 //        }
-        if (isDefaultNetwork(networkAgent)) {
+        if (isFallbackNetwork(networkAgent)) {
             updateTcpBufferSizes(newLp.getTcpBufferSizes());
         }
 
@@ -6233,7 +6246,7 @@
         // updateDnses will fetch the private DNS configuration from DnsManager.
         mDnsManager.updatePrivateDnsStatus(netId, newLp);
 
-        if (isDefaultNetwork(networkAgent)) {
+        if (isFallbackNetwork(networkAgent)) {
             handleApplyDefaultProxy(newLp.getHttpProxy());
         } else {
             updateProxy(newLp, oldLp);
@@ -7184,14 +7197,41 @@
         }
     }
 
-    private void makeDefault(@Nullable final NetworkAgentInfo newNetwork) {
-        if (DBG) log("Switching to new default network: " + newNetwork);
+    private void processDefaultNetworkChanges(@NonNull final NetworkReassignment changes) {
+        boolean isDefaultChanged = false;
+        for (final NetworkRequestInfo defaultRequestInfo : mDefaultNetworkRequests) {
+            final NetworkReassignment.RequestReassignment reassignment =
+                    changes.getReassignment(defaultRequestInfo);
+            if (null == reassignment) {
+                continue;
+            }
+            // reassignment only contains those instances where the satisfying network changed.
+            isDefaultChanged = true;
+            // Notify system services of the new default.
+            makeDefault(defaultRequestInfo, reassignment.mOldNetwork, reassignment.mNewNetwork);
+        }
 
-        mDefaultNetworkNai = newNetwork;
+        if (isDefaultChanged) {
+            // Hold a wakelock for a short time to help apps in migrating to a new default.
+            scheduleReleaseNetworkTransitionWakelock();
+        }
+    }
+
+    private void makeDefault(@NonNull final NetworkRequestInfo nri,
+            @Nullable final NetworkAgentInfo oldDefaultNetwork,
+            @Nullable final NetworkAgentInfo newDefaultNetwork) {
+        if (DBG) {
+            log("Switching to new default network for: " + nri + " using " + newDefaultNetwork);
+        }
 
         try {
-            if (null != newNetwork) {
-                mNetd.networkSetDefault(newNetwork.network.getNetId());
+            // TODO http://b/176191930 update netd calls in follow-up CL for multinetwork changes.
+            if (mFallbackRequest != nri) {
+                return;
+            }
+
+            if (null != newDefaultNetwork) {
+                mNetd.networkSetDefault(newDefaultNetwork.network.getNetId());
             } else {
                 mNetd.networkClearDefault();
             }
@@ -7199,16 +7239,41 @@
             loge("Exception setting default network :" + e);
         }
 
-        notifyLockdownVpn(newNetwork);
-        handleApplyDefaultProxy(null != newNetwork
-                ? newNetwork.linkProperties.getHttpProxy() : null);
-        updateTcpBufferSizes(null != newNetwork
-                ? newNetwork.linkProperties.getTcpBufferSizes() : null);
+        if (oldDefaultNetwork != null) {
+            mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork);
+        }
+        mNetworkActivityTracker.updateDataActivityTracking(newDefaultNetwork, oldDefaultNetwork);
+        notifyLockdownVpn(newDefaultNetwork);
+        handleApplyDefaultProxy(null != newDefaultNetwork
+                ? newDefaultNetwork.linkProperties.getHttpProxy() : null);
+        updateTcpBufferSizes(null != newDefaultNetwork
+                ? newDefaultNetwork.linkProperties.getTcpBufferSizes() : null);
         notifyIfacesChangedForNetworkStats();
         // Fix up the NetworkCapabilities of any networks that have this network as underlying.
-        if (newNetwork != null) {
-            propagateUnderlyingNetworkCapabilities(newNetwork.network);
+        if (newDefaultNetwork != null) {
+            propagateUnderlyingNetworkCapabilities(newDefaultNetwork.network);
         }
+
+        // Log 0 -> X and Y -> X default network transitions, where X is the new default.
+        final Network network = (newDefaultNetwork != null) ? newDefaultNetwork.network : null;
+        final int score = (newDefaultNetwork != null) ? newDefaultNetwork.getCurrentScore() : 0;
+        final boolean validated = newDefaultNetwork != null && newDefaultNetwork.lastValidated;
+        final LinkProperties lp = (newDefaultNetwork != null)
+                ? newDefaultNetwork.linkProperties : null;
+        final NetworkCapabilities nc = (newDefaultNetwork != null)
+                ? newDefaultNetwork.networkCapabilities : null;
+
+        final Network prevNetwork = (oldDefaultNetwork != null)
+                ? oldDefaultNetwork.network : null;
+        final int prevScore = (oldDefaultNetwork != null)
+                ? oldDefaultNetwork.getCurrentScore() : 0;
+        final LinkProperties prevLp = (oldDefaultNetwork != null)
+                ? oldDefaultNetwork.linkProperties : null;
+        final NetworkCapabilities prevNc = (oldDefaultNetwork != null)
+                ? oldDefaultNetwork.networkCapabilities : null;
+
+        mMetricsLog.logDefaultNetworkEvent(network, score, validated, lp, nc,
+                prevNetwork, prevScore, prevLp, prevNc);
     }
 
     private void processListenRequests(@NonNull final NetworkAgentInfo nai) {
@@ -7460,46 +7525,8 @@
                     now);
         }
 
-        final NetworkAgentInfo oldDefaultNetwork = getDefaultNetwork();
-        final NetworkRequestInfo defaultRequestInfo = mNetworkRequests.get(mDefaultRequest);
-        final NetworkReassignment.RequestReassignment reassignment =
-                changes.getReassignment(defaultRequestInfo);
-        final NetworkAgentInfo newDefaultNetwork =
-                null != reassignment ? reassignment.mNewNetwork : oldDefaultNetwork;
-
-        if (oldDefaultNetwork != newDefaultNetwork) {
-            if (oldDefaultNetwork != null) {
-                mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork);
-            }
-            mNetworkActivityTracker.updateDataActivityTracking(
-                    newDefaultNetwork, oldDefaultNetwork);
-            // Notify system services of the new default.
-            makeDefault(newDefaultNetwork);
-
-            // Log 0 -> X and Y -> X default network transitions, where X is the new default.
-            final Network network = (newDefaultNetwork != null) ? newDefaultNetwork.network : null;
-            final int score = (newDefaultNetwork != null) ? newDefaultNetwork.getCurrentScore() : 0;
-            final boolean validated = newDefaultNetwork != null && newDefaultNetwork.lastValidated;
-            final LinkProperties lp = (newDefaultNetwork != null)
-                    ? newDefaultNetwork.linkProperties : null;
-            final NetworkCapabilities nc = (newDefaultNetwork != null)
-                    ? newDefaultNetwork.networkCapabilities : null;
-
-            final Network prevNetwork = (oldDefaultNetwork != null)
-                    ? oldDefaultNetwork.network : null;
-            final int prevScore = (oldDefaultNetwork != null)
-                    ? oldDefaultNetwork.getCurrentScore() : 0;
-            final LinkProperties prevLp = (oldDefaultNetwork != null)
-                    ? oldDefaultNetwork.linkProperties : null;
-            final NetworkCapabilities prevNc = (oldDefaultNetwork != null)
-                    ? oldDefaultNetwork.networkCapabilities : null;
-
-            mMetricsLog.logDefaultNetworkEvent(network, score, validated, lp, nc,
-                    prevNetwork, prevScore, prevLp, prevNc);
-
-            // Have a new default network, release the transition wakelock in
-            scheduleReleaseNetworkTransitionWakelock();
-        }
+        // Process default network changes if applicable.
+        processDefaultNetworkChanges(changes);
 
         // Notify requested networks are available after the default net is switched, but
         // before LegacyTypeTracker sends legacy broadcasts
@@ -7552,7 +7579,7 @@
             notifyNetworkLosing(nai, now);
         }
 
-        updateLegacyTypeTrackerAndVpnLockdownForRematch(oldDefaultNetwork, newDefaultNetwork, nais);
+        updateLegacyTypeTrackerAndVpnLockdownForRematch(changes, nais);
 
         // Tear down all unneeded networks.
         for (NetworkAgentInfo nai : mNetworkAgentInfos) {
@@ -7595,29 +7622,36 @@
     }
 
     private void updateLegacyTypeTrackerAndVpnLockdownForRematch(
-            @Nullable final NetworkAgentInfo oldDefaultNetwork,
-            @Nullable final NetworkAgentInfo newDefaultNetwork,
+            @NonNull final NetworkReassignment changes,
             @NonNull final Collection<NetworkAgentInfo> nais) {
-        if (oldDefaultNetwork != newDefaultNetwork) {
+        final NetworkReassignment.RequestReassignment fallbackReassignment =
+                changes.getReassignment(mFallbackRequest);
+        final NetworkAgentInfo oldFallbackNetwork =
+                null != fallbackReassignment ? fallbackReassignment.mOldNetwork : null;
+        final NetworkAgentInfo newFallbackNetwork =
+                null != fallbackReassignment ? fallbackReassignment.mNewNetwork : null;
+
+        if (oldFallbackNetwork != newFallbackNetwork) {
             // 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
             // old default network before they see a connected broadcast for the new one.
-            if (oldDefaultNetwork != null) {
-                mLegacyTypeTracker.remove(oldDefaultNetwork.networkInfo.getType(),
-                        oldDefaultNetwork, true);
+            if (oldFallbackNetwork != null) {
+                mLegacyTypeTracker.remove(oldFallbackNetwork.networkInfo.getType(),
+                        oldFallbackNetwork, true);
             }
-            if (newDefaultNetwork != null) {
+            if (newFallbackNetwork != null) {
                 // The new default network can be newly null if and only if the old default
                 // network doesn't satisfy the default request any more because it lost a
                 // capability.
-                mDefaultInetConditionPublished = newDefaultNetwork.lastValidated ? 100 : 0;
-                mLegacyTypeTracker.add(newDefaultNetwork.networkInfo.getType(), newDefaultNetwork);
+                mDefaultInetConditionPublished = newFallbackNetwork.lastValidated ? 100 : 0;
+                mLegacyTypeTracker.add(
+                        newFallbackNetwork.networkInfo.getType(), newFallbackNetwork);
                 // If the legacy VPN is connected, notifyLockdownVpn may end up sending a broadcast
                 // to reflect the NetworkInfo of this new network. This broadcast has to be sent
                 // after the disconnect broadcasts above, but before the broadcasts sent by the
                 // legacy type tracker below.
                 // TODO : refactor this, it's too complex
-                notifyLockdownVpn(newDefaultNetwork);
+                notifyLockdownVpn(newFallbackNetwork);
             }
         }
 
@@ -7652,7 +7686,7 @@
         }
 
         // A VPN generally won't get added to the legacy tracker in the "for (nri)" loop above,
-        // because usually there are no NetworkRequests it satisfies (e.g., mDefaultRequest
+        // because usually there are no NetworkRequests it satisfies (e.g., mFallbackRequest
         // wants the NOT_VPN capability, so it will never be satisfied by a VPN). So, add the
         // newNetwork to the tracker explicitly (it's a no-op if it has already been added).
         if (nai.isVPN()) {
@@ -7663,9 +7697,9 @@
     private void updateInetCondition(NetworkAgentInfo nai) {
         // Don't bother updating until we've graduated to validated at least once.
         if (!nai.everValidated) return;
-        // For now only update icons for default connection.
+        // For now only update icons for the fallback connection.
         // TODO: Update WiFi and cellular icons separately. b/17237507
-        if (!isDefaultNetwork(nai)) return;
+        if (!isFallbackNetwork(nai)) return;
 
         int newInetCondition = nai.lastValidated ? 100 : 0;
         // Don't repeat publish.
@@ -7933,8 +7967,8 @@
                 intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
             }
             NetworkAgentInfo newDefaultAgent = null;
-            if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
-                newDefaultAgent = getDefaultNetwork();
+            if (nai.isSatisfyingRequest(mFallbackRequest.mRequests.get(0).requestId)) {
+                newDefaultAgent = getFallbackNetwork();
                 if (newDefaultAgent != null) {
                     intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO,
                             newDefaultAgent.networkInfo);
@@ -7981,10 +8015,10 @@
      */
     private Network[] getDefaultNetworks() {
         ensureRunningOnConnectivityServiceThread();
-        ArrayList<Network> defaultNetworks = new ArrayList<>();
-        NetworkAgentInfo defaultNetwork = getDefaultNetwork();
+        final ArrayList<Network> defaultNetworks = new ArrayList<>();
+        final NetworkAgentInfo fallbackNetwork = getFallbackNetwork();
         for (NetworkAgentInfo nai : mNetworkAgentInfos) {
-            if (nai.everConnected && (nai == defaultNetwork || nai.isVPN())) {
+            if (nai.everConnected && (nai == fallbackNetwork || nai.isVPN())) {
                 defaultNetworks.add(nai.network);
             }
         }
@@ -8034,7 +8068,6 @@
         int user = UserHandle.getUserId(mDeps.getCallingUid());
         final boolean success;
         synchronized (mVpns) {
-            throwIfLockdownEnabled();
             success = mVpns.get(user).setUnderlyingNetworks(networks);
         }
         return success;
diff --git a/services/core/java/com/android/server/connectivity/PacProxyInstaller.java b/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
index 5dc8c1a..aadaf4d 100644
--- a/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
+++ b/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
@@ -16,7 +16,6 @@
 
 package com.android.server.connectivity;
 
-import android.annotation.NonNull;
 import android.annotation.WorkerThread;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -72,6 +71,10 @@
     private static final int DELAY_LONG = 4;
     private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
 
+    // Return values for #setCurrentProxyScriptUrl
+    public static final boolean DONT_SEND_BROADCAST = false;
+    public static final boolean DO_SEND_BROADCAST = true;
+
     private String mCurrentPac;
     @GuardedBy("mProxyLock")
     private volatile Uri mPacUrl = Uri.EMPTY;
@@ -90,7 +93,7 @@
     private volatile boolean mHasSentBroadcast;
     private volatile boolean mHasDownloaded;
 
-    private final Handler mConnectivityHandler;
+    private Handler mConnectivityHandler;
     private final int mProxyMessage;
 
     /**
@@ -99,13 +102,6 @@
     private final Object mProxyLock = new Object();
 
     /**
-     * Lock ensuring consistency between the values of mHasSentBroadcast, mHasDownloaded, the
-     * last URL and port, and the broadcast message being sent with the correct arguments.
-     * TODO : this should probably protect all instances of these variables
-     */
-    private final Object mBroadcastStateLock = new Object();
-
-    /**
      * Runnable to download PAC script.
      * The behavior relies on the assumption it always runs on mNetThread to guarantee that the
      * latest data fetched from mPacUrl is stored in mProxyService.
@@ -150,7 +146,7 @@
         }
     }
 
-    public PacProxyInstaller(@NonNull Context context, @NonNull Handler handler, int proxyMessage) {
+    public PacProxyInstaller(Context context, Handler handler, int proxyMessage) {
         mContext = context;
         mLastPort = -1;
         final HandlerThread netThread = new HandlerThread("android.pacproxyinstaller",
@@ -180,27 +176,31 @@
      * PacProxyInstaller will trigger a new broadcast when it is ready.
      *
      * @param proxy Proxy information that is about to be broadcast.
+     * @return Returns whether the broadcast should be sent : either DO_ or DONT_SEND_BROADCAST
      */
-    public void setCurrentProxyScriptUrl(@NonNull ProxyInfo proxy) {
-        synchronized (mBroadcastStateLock) {
-            if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
-                if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) return;
-                mPacUrl = proxy.getPacFileUrl();
-                mCurrentDelay = DELAY_1;
-                mHasSentBroadcast = false;
-                mHasDownloaded = false;
-                getAlarmManager().cancel(mPacRefreshIntent);
-                bind();
-            } else {
-                getAlarmManager().cancel(mPacRefreshIntent);
-                synchronized (mProxyLock) {
-                    mPacUrl = Uri.EMPTY;
-                    mCurrentPac = null;
-                    if (mProxyService != null) {
-                        unbind();
-                    }
+    public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
+        if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
+            if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
+                // Allow to send broadcast, nothing to do.
+                return DO_SEND_BROADCAST;
+            }
+            mPacUrl = proxy.getPacFileUrl();
+            mCurrentDelay = DELAY_1;
+            mHasSentBroadcast = false;
+            mHasDownloaded = false;
+            getAlarmManager().cancel(mPacRefreshIntent);
+            bind();
+            return DONT_SEND_BROADCAST;
+        } else {
+            getAlarmManager().cancel(mPacRefreshIntent);
+            synchronized (mProxyLock) {
+                mPacUrl = Uri.EMPTY;
+                mCurrentPac = null;
+                if (mProxyService != null) {
+                    unbind();
                 }
             }
+            return DO_SEND_BROADCAST;
         }
     }
 
@@ -275,7 +275,6 @@
         getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
     }
 
-    @GuardedBy("mProxyLock")
     private void setCurrentProxyScript(String script) {
         if (mProxyService == null) {
             Log.e(TAG, "setCurrentProxyScript: no proxy service");
@@ -348,9 +347,6 @@
                             public void setProxyPort(int port) {
                                 if (mLastPort != -1) {
                                     // Always need to send if port changed
-                                    // TODO: Here lacks synchronization because this write cannot
-                                    // guarantee that it's visible from sendProxyIfNeeded() when
-                                    // it's called by a Runnable which is post by mNetThread.
                                     mHasSentBroadcast = false;
                                 }
                                 mLastPort = port;
@@ -390,15 +386,13 @@
         mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
     }
 
-    private void sendProxyIfNeeded() {
-        synchronized (mBroadcastStateLock) {
-            if (!mHasDownloaded || (mLastPort == -1)) {
-                return;
-            }
-            if (!mHasSentBroadcast) {
-                sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort));
-                mHasSentBroadcast = true;
-            }
+    private synchronized void sendProxyIfNeeded() {
+        if (!mHasDownloaded || (mLastPort == -1)) {
+            return;
+        }
+        if (!mHasSentBroadcast) {
+            sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort));
+            mHasSentBroadcast = true;
         }
     }
 }
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index b618d2b..d83ff83 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -226,9 +226,9 @@
         final ProxyInfo defaultProxy = getDefaultProxy();
         final ProxyInfo proxyInfo = null != defaultProxy ?
                 defaultProxy : ProxyInfo.buildDirectProxy("", 0, Collections.emptyList());
-        mPacProxyInstaller.setCurrentProxyScriptUrl(proxyInfo);
 
-        if (!shouldSendBroadcast(proxyInfo)) {
+        if (mPacProxyInstaller.setCurrentProxyScriptUrl(proxyInfo)
+                == PacProxyInstaller.DONT_SEND_BROADCAST) {
             return;
         }
         if (DBG) Log.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
@@ -244,13 +244,6 @@
         }
     }
 
-    private boolean shouldSendBroadcast(ProxyInfo proxy) {
-        if (Uri.EMPTY.equals(proxy.getPacFileUrl())) return false;
-        if (proxy.getPacFileUrl().equals(proxy.getPacFileUrl())
-                && (proxy.getPort() > 0)) return true;
-        return true;
-    }
-
     /**
      * Sets the global proxy in memory. Also writes the values to the global settings of the device.
      *
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index b455a3f..d956ba3 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1982,27 +1982,28 @@
      * secondary thread to perform connection work, returning quickly.
      *
      * Should only be called to respond to Binder requests as this enforces caller permission. Use
-     * {@link #startLegacyVpnPrivileged(VpnProfile, KeyStore, LinkProperties)} to skip the
+     * {@link #startLegacyVpnPrivileged(VpnProfile, KeyStore, Network, LinkProperties)} to skip the
      * permission check only when the caller is trusted (or the call is initiated by the system).
      */
-    public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
+    public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, @Nullable Network underlying,
+            LinkProperties egress) {
         enforceControlPermission();
         final long token = Binder.clearCallingIdentity();
         try {
-            startLegacyVpnPrivileged(profile, keyStore, egress);
+            startLegacyVpnPrivileged(profile, keyStore, underlying, egress);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
     }
 
     /**
-     * Like {@link #startLegacyVpn(VpnProfile, KeyStore, LinkProperties)}, but does not check
-     * permissions under the assumption that the caller is the system.
+     * Like {@link #startLegacyVpn(VpnProfile, KeyStore, Network, LinkProperties)}, but does not
+     * check permissions under the assumption that the caller is the system.
      *
      * Callers are responsible for checking permissions if needed.
      */
     public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
-            LinkProperties egress) {
+            @Nullable Network underlying, @NonNull LinkProperties egress) {
         UserManager mgr = UserManager.get(mContext);
         UserInfo user = mgr.getUserInfo(mUserId);
         if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
@@ -2128,6 +2129,9 @@
         config.session = profile.name;
         config.isMetered = false;
         config.proxyInfo = profile.proxy;
+        if (underlying != null) {
+            config.underlyingNetworks = new Network[] { underlying };
+        }
 
         config.addLegacyRoutes(profile.routes);
         if (!profile.dnsServers.isEmpty()) {
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index ea2788c..6d1c680 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -155,7 +155,7 @@
                 try {
                     // Use the privileged method because Lockdown VPN is initiated by the system, so
                     // no additional permission checks are necessary.
-                    mVpn.startLegacyVpnPrivileged(mProfile, mKeyStore, egressProp);
+                    mVpn.startLegacyVpnPrivileged(mProfile, mKeyStore, null, egressProp);
                 } catch (IllegalStateException e) {
                     mAcceptedEgressIface = null;
                     Log.e(TAG, "Failed to start VPN", e);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 01d4faf..0fd39a4 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -239,6 +239,7 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.StatLogger;
 import com.android.internal.util.XmlUtils;
+import com.android.net.module.util.NetworkIdentityUtils;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
@@ -2162,13 +2163,14 @@
             if (template.matches(probeIdent)) {
                 if (LOGD) {
                     Slog.d(TAG, "Found template " + template + " which matches subscriber "
-                            + NetworkIdentity.scrubSubscriberId(subscriberId));
+                            + NetworkIdentityUtils.scrubSubscriberId(subscriberId));
                 }
                 return false;
             }
         }
 
-        Slog.i(TAG, "No policy for subscriber " + NetworkIdentity.scrubSubscriberId(subscriberId)
+        Slog.i(TAG, "No policy for subscriber "
+                + NetworkIdentityUtils.scrubSubscriberId(subscriberId)
                 + "; generating default policy");
         final NetworkPolicy policy = buildDefaultMobilePolicy(subId, subscriberId);
         addNetworkPolicyAL(policy);
@@ -3596,14 +3598,15 @@
                     final int subId = mSubIdToSubscriberId.keyAt(i);
                     final String subscriberId = mSubIdToSubscriberId.valueAt(i);
 
-                    fout.println(subId + "=" + NetworkIdentity.scrubSubscriberId(subscriberId));
+                    fout.println(subId + "="
+                            + NetworkIdentityUtils.scrubSubscriberId(subscriberId));
                 }
                 fout.decreaseIndent();
 
                 fout.println();
                 for (String[] mergedSubscribers : mMergedSubscriberIds) {
                     fout.println("Merged subscriptions: " + Arrays.toString(
-                            NetworkIdentity.scrubSubscriberId(mergedSubscribers)));
+                            NetworkIdentityUtils.scrubSubscriberIds(mergedSubscribers)));
                 }
 
                 fout.println();
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index e630da0..ffde68e 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -36,7 +36,7 @@
         "libvndksupport",
         "libziparchive",
         "libz",
-        "netd_aidl_interface-cpp",
+        "netd_aidl_interface-V5-cpp",
     ],
 }
 
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 4c658e1..b0cc7f1 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -132,6 +132,7 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.AlarmManager;
 import android.app.AppOpsManager;
 import android.app.NotificationManager;
@@ -252,6 +253,7 @@
 import com.android.internal.util.WakeupMessage;
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.net.module.util.ArrayTrackRecord;
 import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
 import com.android.server.connectivity.ConnectivityConstants;
 import com.android.server.connectivity.MockableSystemProperties;
@@ -906,28 +908,69 @@
     }
 
     /**
-     * A NetworkFactory that allows tests to wait until any in-flight NetworkRequest add or remove
-     * operations have been processed. Before ConnectivityService can add or remove any requests,
-     * the factory must be told to expect those operations by calling expectAddRequestsWithScores or
-     * expectRemoveRequests.
+     * A NetworkFactory that allows to wait until any in-flight NetworkRequest add or remove
+     * operations have been processed and test for them.
      */
     private static class MockNetworkFactory extends NetworkFactory {
         private final ConditionVariable mNetworkStartedCV = new ConditionVariable();
         private final ConditionVariable mNetworkStoppedCV = new ConditionVariable();
         private final AtomicBoolean mNetworkStarted = new AtomicBoolean(false);
 
-        // Used to expect that requests be removed or added on a separate thread, without sleeping.
-        // Callers can call either expectAddRequestsWithScores() or expectRemoveRequests() exactly
-        // once, then cause some other thread to add or remove requests, then call
-        // waitForRequests().
-        // It is not possible to wait for both add and remove requests. When adding, the queue
-        // contains the expected score. When removing, the value is unused, all matters is the
-        // number of objects in the queue.
-        private final LinkedBlockingQueue<Integer> mExpectations;
+        static class RequestEntry {
+            @NonNull
+            public final NetworkRequest request;
 
-        // Whether we are currently expecting requests to be added or removed. Valid only if
-        // mExpectations is non-empty.
-        private boolean mExpectingAdditions;
+            RequestEntry(@NonNull final NetworkRequest request) {
+                this.request = request;
+            }
+
+            static final class Add extends RequestEntry {
+                public final int factorySerialNumber;
+
+                Add(@NonNull final NetworkRequest request, final int factorySerialNumber) {
+                    super(request);
+                    this.factorySerialNumber = factorySerialNumber;
+                }
+            }
+
+            static final class Remove extends RequestEntry {
+                Remove(@NonNull final NetworkRequest request) {
+                    super(request);
+                }
+            }
+        }
+
+        // History of received requests adds and removes.
+        private final ArrayTrackRecord<RequestEntry>.ReadHead mRequestHistory =
+                new ArrayTrackRecord<RequestEntry>().newReadHead();
+
+        private static <T> T failIfNull(@Nullable final T obj, @Nullable final String message) {
+            if (null == obj) fail(null != message ? message : "Must not be null");
+            return obj;
+        }
+
+
+        public RequestEntry.Add expectRequestAdd() {
+            return failIfNull((RequestEntry.Add) mRequestHistory.poll(TIMEOUT_MS,
+                    it -> it instanceof RequestEntry.Add), "Expected request add");
+        }
+
+        public void expectRequestAdds(final int count) {
+            for (int i = count; i > 0; --i) {
+                expectRequestAdd();
+            }
+        }
+
+        public RequestEntry.Remove expectRequestRemove() {
+            return failIfNull((RequestEntry.Remove) mRequestHistory.poll(TIMEOUT_MS,
+                    it -> it instanceof RequestEntry.Remove), "Expected request remove");
+        }
+
+        public void expectRequestRemoves(final int count) {
+            for (int i = count; i > 0; --i) {
+                expectRequestRemove();
+            }
+        }
 
         // Used to collect the networks requests managed by this factory. This is a duplicate of
         // the internal information stored in the NetworkFactory (which is private).
@@ -936,7 +979,6 @@
         public MockNetworkFactory(Looper looper, Context context, String logTag,
                 NetworkCapabilities filter) {
             super(looper, context, logTag, filter);
-            mExpectations = new LinkedBlockingQueue<>();
         }
 
         public int getMyRequestCount() {
@@ -970,95 +1012,33 @@
         @Override
         protected void handleAddRequest(NetworkRequest request, int score,
                 int factorySerialNumber) {
-            synchronized (mExpectations) {
-                final Integer expectedScore = mExpectations.poll(); // null if the queue is empty
-
-                assertNotNull("Added more requests than expected (" + request + " score : "
-                        + score + ")", expectedScore);
-                // If we're expecting anything, we must be expecting additions.
-                if (!mExpectingAdditions) {
-                    fail("Can't add requests while expecting requests to be removed");
-                }
-                if (expectedScore != score) {
-                    fail("Expected score was " + expectedScore + " but actual was " + score
-                            + " in added request");
-                }
-
-                // Add the request.
-                mNetworkRequests.put(request.requestId, request);
-                super.handleAddRequest(request, score, factorySerialNumber);
-                mExpectations.notify();
-            }
+            mNetworkRequests.put(request.requestId, request);
+            super.handleAddRequest(request, score, factorySerialNumber);
+            mRequestHistory.add(new RequestEntry.Add(request, factorySerialNumber));
         }
 
         @Override
         protected void handleRemoveRequest(NetworkRequest request) {
-            synchronized (mExpectations) {
-                final Integer expectedScore = mExpectations.poll(); // null if the queue is empty
+            mNetworkRequests.remove(request.requestId);
+            super.handleRemoveRequest(request);
+            mRequestHistory.add(new RequestEntry.Remove(request));
+        }
 
-                assertTrue("Removed more requests than expected", expectedScore != null);
-                // If we're expecting anything, we must be expecting removals.
-                if (mExpectingAdditions) {
-                    fail("Can't remove requests while expecting requests to be added");
-                }
+        public void assertRequestCountEquals(final int count) {
+            assertEquals(count, getMyRequestCount());
+        }
 
-                // Remove the request.
-                mNetworkRequests.remove(request.requestId);
-                super.handleRemoveRequest(request);
-                mExpectations.notify();
-            }
+        @Override
+        public void terminate() {
+            super.terminate();
+            // Make sure there are no remaining requests unaccounted for.
+            assertNull(mRequestHistory.poll(TIMEOUT_MS, r -> true));
         }
 
         // Trigger releasing the request as unfulfillable
         public void triggerUnfulfillable(NetworkRequest r) {
             super.releaseRequestAsUnfulfillableByAnyFactory(r);
         }
-
-        private void assertNoExpectations() {
-            if (mExpectations.size() != 0) {
-                fail("Can't add expectation, " + mExpectations.size() + " already pending");
-            }
-        }
-
-        // Expects that requests with the specified scores will be added.
-        public void expectAddRequestsWithScores(final int... scores) {
-            assertNoExpectations();
-            mExpectingAdditions = true;
-            for (int score : scores) {
-                mExpectations.add(score);
-            }
-        }
-
-        // Expects that count requests will be removed.
-        public void expectRemoveRequests(final int count) {
-            assertNoExpectations();
-            mExpectingAdditions = false;
-            for (int i = 0; i < count; ++i) {
-                mExpectations.add(0); // For removals the score is ignored so any value will do.
-            }
-        }
-
-        // Waits for the expected request additions or removals to happen within a timeout.
-        public void waitForRequests() throws InterruptedException {
-            final long deadline = SystemClock.elapsedRealtime() + TIMEOUT_MS;
-            synchronized (mExpectations) {
-                while (mExpectations.size() > 0 && SystemClock.elapsedRealtime() < deadline) {
-                    mExpectations.wait(deadline - SystemClock.elapsedRealtime());
-                }
-            }
-            final long count = mExpectations.size();
-            final String msg = count + " requests still not " +
-                    (mExpectingAdditions ? "added" : "removed") +
-                    " after " + TIMEOUT_MS + " ms";
-            assertEquals(msg, 0, count);
-        }
-
-        public SparseArray<NetworkRequest> waitForNetworkRequests(final int count)
-                throws InterruptedException {
-            waitForRequests();
-            assertEquals(count, getMyRequestCount());
-            return mNetworkRequests;
-        }
     }
 
     private Set<UidRange> uidRangesForUid(int uid) {
@@ -2595,12 +2575,6 @@
         callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
     }
 
-    private int[] makeIntArray(final int size, final int value) {
-        final int[] array = new int[size];
-        Arrays.fill(array, value);
-        return array;
-    }
-
     private void tryNetworkFactoryRequests(int capability) throws Exception {
         // Verify NOT_RESTRICTED is set appropriately
         final NetworkCapabilities nc = new NetworkRequest.Builder().addCapability(capability)
@@ -2622,9 +2596,9 @@
                 mServiceContext, "testFactory", filter);
         testFactory.setScoreFilter(40);
         ConditionVariable cv = testFactory.getNetworkStartedCV();
-        testFactory.expectAddRequestsWithScores(0);
         testFactory.register();
-        testFactory.waitForNetworkRequests(1);
+        testFactory.expectRequestAdd();
+        testFactory.assertRequestCountEquals(1);
         int expectedRequestCount = 1;
         NetworkCallback networkCallback = null;
         // For non-INTERNET capabilities we cannot rely on the default request being present, so
@@ -2633,13 +2607,12 @@
             assertFalse(testFactory.getMyStartRequested());
             NetworkRequest request = new NetworkRequest.Builder().addCapability(capability).build();
             networkCallback = new NetworkCallback();
-            testFactory.expectAddRequestsWithScores(0);  // New request
             mCm.requestNetwork(request, networkCallback);
             expectedRequestCount++;
-            testFactory.waitForNetworkRequests(expectedRequestCount);
+            testFactory.expectRequestAdd();
         }
         waitFor(cv);
-        assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
+        testFactory.assertRequestCountEquals(expectedRequestCount);
         assertTrue(testFactory.getMyStartRequested());
 
         // Now bring in a higher scored network.
@@ -2653,15 +2626,14 @@
         // When testAgent connects, ConnectivityService will re-send us all current requests with
         // the new score. There are expectedRequestCount such requests, and we must wait for all of
         // them.
-        testFactory.expectAddRequestsWithScores(makeIntArray(expectedRequestCount, 50));
         testAgent.connect(false);
         testAgent.addCapability(capability);
         waitFor(cv);
-        testFactory.waitForNetworkRequests(expectedRequestCount);
+        testFactory.expectRequestAdds(expectedRequestCount);
+        testFactory.assertRequestCountEquals(expectedRequestCount);
         assertFalse(testFactory.getMyStartRequested());
 
         // Bring in a bunch of requests.
-        testFactory.expectAddRequestsWithScores(makeIntArray(10, 50));
         assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
         ConnectivityManager.NetworkCallback[] networkCallbacks =
                 new ConnectivityManager.NetworkCallback[10];
@@ -2671,24 +2643,24 @@
             builder.addCapability(capability);
             mCm.requestNetwork(builder.build(), networkCallbacks[i]);
         }
-        testFactory.waitForNetworkRequests(10 + expectedRequestCount);
+        testFactory.expectRequestAdds(10);
+        testFactory.assertRequestCountEquals(10 + expectedRequestCount);
         assertFalse(testFactory.getMyStartRequested());
 
         // Remove the requests.
-        testFactory.expectRemoveRequests(10);
         for (int i = 0; i < networkCallbacks.length; i++) {
             mCm.unregisterNetworkCallback(networkCallbacks[i]);
         }
-        testFactory.waitForNetworkRequests(expectedRequestCount);
+        testFactory.expectRequestRemoves(10);
+        testFactory.assertRequestCountEquals(expectedRequestCount);
         assertFalse(testFactory.getMyStartRequested());
 
         // Drop the higher scored network.
         cv = testFactory.getNetworkStartedCV();
-        // With the default network disconnecting, the requests are sent with score 0 to factories.
-        testFactory.expectAddRequestsWithScores(makeIntArray(expectedRequestCount, 0));
         testAgent.disconnect();
         waitFor(cv);
-        testFactory.waitForNetworkRequests(expectedRequestCount);
+        testFactory.expectRequestAdds(expectedRequestCount);
+        testFactory.assertRequestCountEquals(expectedRequestCount);
         assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
         assertTrue(testFactory.getMyStartRequested());
 
@@ -2731,9 +2703,8 @@
             final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
                     mServiceContext, "testFactory", filter);
             // Register the factory and don't be surprised when the default request arrives.
-            testFactory.expectAddRequestsWithScores(0);
             testFactory.register();
-            testFactory.waitForNetworkRequests(1);
+            testFactory.expectRequestAdd();
 
             testFactory.setScoreFilter(42);
             testFactory.terminate();
@@ -3876,38 +3847,37 @@
         testFactory.setScoreFilter(40);
 
         // Register the factory and expect it to start looking for a network.
-        testFactory.expectAddRequestsWithScores(0);  // Score 0 as the request is not served yet.
         testFactory.register();
 
         try {
-            testFactory.waitForNetworkRequests(1);
+            testFactory.expectRequestAdd();
+            testFactory.assertRequestCountEquals(1);
             assertTrue(testFactory.getMyStartRequested());
 
             // Bring up wifi. The factory stops looking for a network.
             mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
             // Score 60 - 40 penalty for not validated yet, then 60 when it validates
-            testFactory.expectAddRequestsWithScores(20, 60);
             mWiFiNetworkAgent.connect(true);
-            testFactory.waitForRequests();
+            // Default request and mobile always on request
+            testFactory.expectRequestAdds(2);
             assertFalse(testFactory.getMyStartRequested());
 
-            ContentResolver cr = mServiceContext.getContentResolver();
-
             // Turn on mobile data always on. The factory starts looking again.
-            testFactory.expectAddRequestsWithScores(0);  // Always on requests comes up with score 0
             setAlwaysOnNetworks(true);
-            testFactory.waitForNetworkRequests(2);
+            testFactory.expectRequestAdd();
+            testFactory.assertRequestCountEquals(2);
+
             assertTrue(testFactory.getMyStartRequested());
 
             // Bring up cell data and check that the factory stops looking.
             assertLength(1, mCm.getAllNetworks());
             mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
-            testFactory.expectAddRequestsWithScores(10, 50);  // Unvalidated, then validated
             mCellNetworkAgent.connect(true);
             cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
-            testFactory.waitForNetworkRequests(2);
-            assertFalse(
-                    testFactory.getMyStartRequested());  // Because the cell network outscores us.
+            testFactory.expectRequestAdds(2); // Unvalidated and validated
+            testFactory.assertRequestCountEquals(2);
+            // The cell network outscores the factory filter, so start is not requested.
+            assertFalse(testFactory.getMyStartRequested());
 
             // Check that cell data stays up.
             waitForIdle();
@@ -3915,9 +3885,8 @@
             assertLength(2, mCm.getAllNetworks());
 
             // Turn off mobile data always on and expect the request to disappear...
-            testFactory.expectRemoveRequests(1);
             setAlwaysOnNetworks(false);
-            testFactory.waitForNetworkRequests(1);
+            testFactory.expectRequestRemove();
 
             // ...  and cell data to be torn down.
             cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
@@ -4224,46 +4193,33 @@
         testFactory.setScoreFilter(40);
 
         // Register the factory and expect it to receive the default request.
-        testFactory.expectAddRequestsWithScores(0);
         testFactory.register();
-        SparseArray<NetworkRequest> requests = testFactory.waitForNetworkRequests(1);
-
-        assertEquals(1, requests.size()); // have 1 request at this point
-        int origRequestId = requests.valueAt(0).requestId;
+        testFactory.expectRequestAdd();
 
         // Now file the test request and expect it.
-        testFactory.expectAddRequestsWithScores(0);
         mCm.requestNetwork(nr, networkCallback);
-        requests = testFactory.waitForNetworkRequests(2); // have 2 requests at this point
+        final NetworkRequest newRequest = testFactory.expectRequestAdd().request;
 
-        int newRequestId = 0;
-        for (int i = 0; i < requests.size(); ++i) {
-            if (requests.valueAt(i).requestId != origRequestId) {
-                newRequestId = requests.valueAt(i).requestId;
-                break;
-            }
-        }
-
-        testFactory.expectRemoveRequests(1);
         if (preUnregister) {
             mCm.unregisterNetworkCallback(networkCallback);
 
             // Simulate the factory releasing the request as unfulfillable: no-op since
             // the callback has already been unregistered (but a test that no exceptions are
             // thrown).
-            testFactory.triggerUnfulfillable(requests.get(newRequestId));
+            testFactory.triggerUnfulfillable(newRequest);
         } else {
             // Simulate the factory releasing the request as unfulfillable and expect onUnavailable!
-            testFactory.triggerUnfulfillable(requests.get(newRequestId));
+            testFactory.triggerUnfulfillable(newRequest);
 
             networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null);
-            testFactory.waitForRequests();
 
             // unregister network callback - a no-op (since already freed by the
             // on-unavailable), but should not fail or throw exceptions.
             mCm.unregisterNetworkCallback(networkCallback);
         }
 
+        testFactory.expectRequestRemove();
+
         testFactory.terminate();
         handlerThread.quit();
     }
diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
index a10a3c8..e590fb7 100644
--- a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
+++ b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
@@ -55,7 +55,7 @@
     private val supportedTypes = arrayOf(TYPE_MOBILE, TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_SUPL)
 
     private val mMockService = mock(ConnectivityService::class.java).apply {
-        doReturn(false).`when`(this).isDefaultNetwork(any())
+        doReturn(false).`when`(this).isFallbackNetwork(any())
     }
     private val mTracker = LegacyTypeTracker(mMockService).apply {
         supportedTypes.forEach {
@@ -126,11 +126,11 @@
     fun testBroadcastOnDisconnect() {
         val mobileNai1 = mock(NetworkAgentInfo::class.java)
         val mobileNai2 = mock(NetworkAgentInfo::class.java)
-        doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai1)
+        doReturn(false).`when`(mMockService).isFallbackNetwork(mobileNai1)
         mTracker.add(TYPE_MOBILE, mobileNai1)
         verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, CONNECTED, TYPE_MOBILE)
         reset(mMockService)
-        doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai2)
+        doReturn(false).`when`(mMockService).isFallbackNetwork(mobileNai2)
         mTracker.add(TYPE_MOBILE, mobileNai2)
         verify(mMockService, never()).sendLegacyNetworkBroadcast(any(), any(), anyInt())
         mTracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */)
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 68aaaed..f478282 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 @@
         managedProfileA.profileGroupId = primaryUser.id;
     }
 
+    static final Network EGRESS_NETWORK = new Network(101);
     static final String EGRESS_IFACE = "wlan0";
     static final String TEST_VPN_PKG = "com.testvpn.vpn";
     private static final String TEST_VPN_SERVER = "1.2.3.4";
@@ -963,7 +964,7 @@
                         InetAddresses.parseNumericAddress("192.0.2.0"), EGRESS_IFACE);
         lp.addRoute(defaultRoute);
 
-        vpn.startLegacyVpn(vpnProfile, mKeyStore, lp);
+        vpn.startLegacyVpn(vpnProfile, mKeyStore, EGRESS_NETWORK, lp);
         return vpn;
     }