Merge "Fix title of PRIVATE_DNS_BROKEN notification when connecting VPN"
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 75af41c..56bacf2 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -52,13 +52,12 @@
 import android.os.ServiceSpecificException;
 import android.provider.Settings;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.SparseIntArray;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.telephony.ITelephony;
-import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.Protocol;
 
@@ -711,6 +710,12 @@
     @Deprecated
     public static final int TYPE_TEST = 18; // TODO: Remove this once NetworkTypes are unused.
 
+    // Deprecated constants for return values of startUsingNetworkFeature. They used to live
+    // in com.android.internal.telephony.PhoneConstants until they were made inaccessible.
+    private static final int DEPRECATED_PHONE_CONSTANT_APN_ALREADY_ACTIVE = 0;
+    private static final int DEPRECATED_PHONE_CONSTANT_APN_REQUEST_STARTED = 1;
+    private static final int DEPRECATED_PHONE_CONSTANT_APN_REQUEST_FAILED = 3;
+
     /** {@hide} */
     public static final int MAX_RADIO_TYPE = TYPE_TEST;
 
@@ -1407,7 +1412,7 @@
         if (netCap == null) {
             Log.d(TAG, "Can't satisfy startUsingNetworkFeature for " + networkType + ", " +
                     feature);
-            return PhoneConstants.APN_REQUEST_FAILED;
+            return DEPRECATED_PHONE_CONSTANT_APN_REQUEST_FAILED;
         }
 
         NetworkRequest request = null;
@@ -1417,9 +1422,9 @@
                 Log.d(TAG, "renewing startUsingNetworkFeature request " + l.networkRequest);
                 renewRequestLocked(l);
                 if (l.currentNetwork != null) {
-                    return PhoneConstants.APN_ALREADY_ACTIVE;
+                    return DEPRECATED_PHONE_CONSTANT_APN_ALREADY_ACTIVE;
                 } else {
-                    return PhoneConstants.APN_REQUEST_STARTED;
+                    return DEPRECATED_PHONE_CONSTANT_APN_REQUEST_STARTED;
                 }
             }
 
@@ -1427,10 +1432,10 @@
         }
         if (request != null) {
             Log.d(TAG, "starting startUsingNetworkFeature for request " + request);
-            return PhoneConstants.APN_REQUEST_STARTED;
+            return DEPRECATED_PHONE_CONSTANT_APN_REQUEST_STARTED;
         } else {
             Log.d(TAG, " request Failed");
-            return PhoneConstants.APN_REQUEST_FAILED;
+            return DEPRECATED_PHONE_CONSTANT_APN_REQUEST_FAILED;
         }
     }
 
@@ -2149,19 +2154,14 @@
     @Deprecated
     @UnsupportedAppUsage
     public boolean getMobileDataEnabled() {
-        IBinder b = ServiceManager.getService(Context.TELEPHONY_SERVICE);
-        if (b != null) {
-            try {
-                ITelephony it = ITelephony.Stub.asInterface(b);
-                int subId = SubscriptionManager.getDefaultDataSubscriptionId();
-                Log.d("ConnectivityManager", "getMobileDataEnabled()+ subId=" + subId);
-                boolean retVal = it.isUserDataEnabled(subId);
-                Log.d("ConnectivityManager", "getMobileDataEnabled()- subId=" + subId
-                        + " retVal=" + retVal);
-                return retVal;
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
+        TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+        if (tm != null) {
+            int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+            Log.d("ConnectivityManager", "getMobileDataEnabled()+ subId=" + subId);
+            boolean retVal = tm.createForSubscriptionId(subId).isDataEnabled();
+            Log.d("ConnectivityManager", "getMobileDataEnabled()- subId=" + subId
+                    + " retVal=" + retVal);
+            return retVal;
         }
         Log.d("ConnectivityManager", "getMobileDataEnabled()- remote exception retVal=false");
         return false;
diff --git a/core/java/android/net/IpConfiguration.java b/core/java/android/net/IpConfiguration.java
index 2af82d7..143467b 100644
--- a/core/java/android/net/IpConfiguration.java
+++ b/core/java/android/net/IpConfiguration.java
@@ -16,6 +16,10 @@
 
 package android.net;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.net.StaticIpConfiguration;
 import android.os.Parcel;
@@ -27,13 +31,17 @@
  * A class representing a configured network.
  * @hide
  */
-public class IpConfiguration implements Parcelable {
+@SystemApi
+public final class IpConfiguration implements Parcelable {
     private static final String TAG = "IpConfiguration";
 
+    // This enum has been used by apps through reflection for many releases.
+    // Therefore they can't just be removed. Duplicating these constants to
+    // give an alternate SystemApi is a worse option than exposing them.
+    @SuppressLint("Enum")
     public enum IpAssignment {
         /* Use statically configured IP settings. Configuration can be accessed
          * with staticIpConfiguration */
-        @UnsupportedAppUsage
         STATIC,
         /* Use dynamically configured IP settings */
         DHCP,
@@ -42,14 +50,19 @@
         UNASSIGNED
     }
 
+    /** @hide */
     public IpAssignment ipAssignment;
 
+    /** @hide */
     public StaticIpConfiguration staticIpConfiguration;
 
+    // This enum has been used by apps through reflection for many releases.
+    // Therefore they can't just be removed. Duplicating these constants to
+    // give an alternate SystemApi is a worse option than exposing them.
+    @SuppressLint("Enum")
     public enum ProxySettings {
         /* No proxy is to be used. Any existing proxy settings
          * should be cleared. */
-        @UnsupportedAppUsage
         NONE,
         /* Use statically configured proxy. Configuration can be accessed
          * with httpProxy. */
@@ -62,8 +75,10 @@
         PAC
     }
 
+    /** @hide */
     public ProxySettings proxySettings;
 
+    /** @hide */
     @UnsupportedAppUsage
     public ProxyInfo httpProxy;
 
@@ -83,6 +98,7 @@
         init(IpAssignment.UNASSIGNED, ProxySettings.UNASSIGNED, null, null);
     }
 
+    /** @hide */
     @UnsupportedAppUsage
     public IpConfiguration(IpAssignment ipAssignment,
                            ProxySettings proxySettings,
@@ -91,7 +107,7 @@
         init(ipAssignment, proxySettings, staticIpConfiguration, httpProxy);
     }
 
-    public IpConfiguration(IpConfiguration source) {
+    public IpConfiguration(@NonNull IpConfiguration source) {
         this();
         if (source != null) {
             init(source.ipAssignment, source.proxySettings,
@@ -99,35 +115,35 @@
         }
     }
 
-    public IpAssignment getIpAssignment() {
+    public @NonNull IpAssignment getIpAssignment() {
         return ipAssignment;
     }
 
-    public void setIpAssignment(IpAssignment ipAssignment) {
+    public void setIpAssignment(@NonNull IpAssignment ipAssignment) {
         this.ipAssignment = ipAssignment;
     }
 
-    public StaticIpConfiguration getStaticIpConfiguration() {
+    public @Nullable StaticIpConfiguration getStaticIpConfiguration() {
         return staticIpConfiguration;
     }
 
-    public void setStaticIpConfiguration(StaticIpConfiguration staticIpConfiguration) {
+    public void setStaticIpConfiguration(@Nullable StaticIpConfiguration staticIpConfiguration) {
         this.staticIpConfiguration = staticIpConfiguration;
     }
 
-    public ProxySettings getProxySettings() {
+    public @NonNull ProxySettings getProxySettings() {
         return proxySettings;
     }
 
-    public void setProxySettings(ProxySettings proxySettings) {
+    public void setProxySettings(@NonNull ProxySettings proxySettings) {
         this.proxySettings = proxySettings;
     }
 
-    public ProxyInfo getHttpProxy() {
+    public @Nullable ProxyInfo getHttpProxy() {
         return httpProxy;
     }
 
-    public void setHttpProxy(ProxyInfo httpProxy) {
+    public void setHttpProxy(@Nullable ProxyInfo httpProxy) {
         this.httpProxy = httpProxy;
     }
 
@@ -175,13 +191,19 @@
                83 * httpProxy.hashCode();
     }
 
-    /** Implement the Parcelable interface */
+    /**
+     * Implement the Parcelable interface
+     * @hide
+     */
     public int describeContents() {
         return 0;
     }
 
-    /** Implement the Parcelable interface  */
-    public void writeToParcel(Parcel dest, int flags) {
+    /**
+     * Implement the Parcelable interface
+     * @hide
+     */
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString(ipAssignment.name());
         dest.writeString(proxySettings.name());
         dest.writeParcelable(staticIpConfiguration, flags);
@@ -189,7 +211,7 @@
     }
 
     /** Implement the Parcelable interface */
-    public static final @android.annotation.NonNull Creator<IpConfiguration> CREATOR =
+    public static final @NonNull Creator<IpConfiguration> CREATOR =
         new Creator<IpConfiguration>() {
             public IpConfiguration createFromParcel(Parcel in) {
                 IpConfiguration config = new IpConfiguration();
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index e3259ff..88877e2 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -478,6 +478,7 @@
      * @return an array of capability values for this instance.
      * @hide
      */
+    @UnsupportedAppUsage
     @TestApi
     public @NetCapability int[] getCapabilities() {
         return BitUtils.unpackBits(mNetworkCapabilities);
diff --git a/core/java/android/net/NetworkScore.java b/core/java/android/net/NetworkScore.java
index 1ab6335..13f2994 100644
--- a/core/java/android/net/NetworkScore.java
+++ b/core/java/android/net/NetworkScore.java
@@ -154,4 +154,9 @@
         }
         return true;
     }
+
+    /** Convert to a string */
+    public String toString() {
+        return "NetworkScore[" + mExtensions.toString() + "]";
+    }
 }
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index 807c467..9d92db4 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -16,7 +16,8 @@
 
 package android.net;
 
-
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -89,6 +90,15 @@
     }
 
     /**
+     * Construct a {@link ProxyInfo} object that will download and run the PAC script at the
+     * specified URL and port.
+     */
+    @NonNull
+    public static ProxyInfo buildPacProxy(@NonNull Uri pacUrl, int port) {
+        return new ProxyInfo(pacUrl, port);
+    }
+
+    /**
      * Create a ProxyProperties that points at a HTTP Proxy.
      * @hide
      */
@@ -105,7 +115,7 @@
      * Create a ProxyProperties that points at a PAC URL.
      * @hide
      */
-    public ProxyInfo(Uri pacFileUrl) {
+    public ProxyInfo(@NonNull Uri pacFileUrl) {
         mHost = LOCAL_HOST;
         mPort = LOCAL_PORT;
         mExclusionList = LOCAL_EXCL_LIST;
@@ -132,7 +142,7 @@
      * Only used in PacManager after Local Proxy is bound.
      * @hide
      */
-    public ProxyInfo(Uri pacFileUrl, int localProxyPort) {
+    public ProxyInfo(@NonNull Uri pacFileUrl, int localProxyPort) {
         mHost = LOCAL_HOST;
         mPort = localProxyPort;
         mExclusionList = LOCAL_EXCL_LIST;
@@ -159,11 +169,10 @@
         mPacFileUrl = Uri.EMPTY;
     }
 
-    // copy constructor instead of clone
     /**
-     * @hide
+     * A copy constructor to hold proxy properties.
      */
-    public ProxyInfo(ProxyInfo source) {
+    public ProxyInfo(@Nullable ProxyInfo source) {
         if (source != null) {
             mHost = source.getHost();
             mPort = source.getPort();
@@ -226,12 +235,13 @@
      * comma separated
      * @hide
      */
+    @Nullable
     public String getExclusionListAsString() {
         return mExclusionList;
     }
 
     /**
-     * @hide
+     * Return true if the pattern of proxy is valid, otherwise return false.
      */
     public boolean isValid() {
         if (!Uri.EMPTY.equals(mPacFileUrl)) return true;
diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java
index 5bc9953..990c114 100644
--- a/core/java/android/net/StaticIpConfiguration.java
+++ b/core/java/android/net/StaticIpConfiguration.java
@@ -25,6 +25,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.util.Preconditions;
+
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.List;
@@ -152,6 +154,7 @@
          * @return The {@link Builder} for chaining.
          */
         public @NonNull Builder setDnsServers(@NonNull Iterable<InetAddress> dnsServers) {
+            Preconditions.checkNotNull(dnsServers);
             mDnsServers = dnsServers;
             return this;
         }
@@ -175,8 +178,10 @@
             final StaticIpConfiguration config = new StaticIpConfiguration();
             config.ipAddress = mIpAddress;
             config.gateway = mGateway;
-            for (InetAddress server : mDnsServers) {
-                config.dnsServers.add(server);
+            if (mDnsServers != null) {
+                for (InetAddress server : mDnsServers) {
+                    config.dnsServers.add(server);
+                }
             }
             config.domains = mDomains;
             return config;
diff --git a/core/java/android/net/util/MultinetworkPolicyTracker.java b/core/java/android/net/util/MultinetworkPolicyTracker.java
index 4e88149..aa0f622 100644
--- a/core/java/android/net/util/MultinetworkPolicyTracker.java
+++ b/core/java/android/net/util/MultinetworkPolicyTracker.java
@@ -94,7 +94,8 @@
             }
         };
 
-        TelephonyManager.from(ctx).listen(new PhoneStateListener(handler.getLooper()) {
+        ctx.getSystemService(TelephonyManager.class).listen(
+                new PhoneStateListener(handler.getLooper()) {
             @Override
             public void onActiveDataSubscriptionIdChanged(int subId) {
                 mActiveSubId = subId;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 81eb4b3..ce4cdc8 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -146,6 +146,7 @@
 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;
@@ -2185,6 +2186,11 @@
      */
     @VisibleForTesting
     public void systemReady() {
+        // Let PermissionMonitor#startMonitoring() running in the beginning of the systemReady
+        // before MultipathPolicyTracker.start(). Since mApps in PermissionMonitor needs to be
+        // populated first to ensure that listening network request which is sent by
+        // MultipathPolicyTracker won't be added NET_CAPABILITY_FOREGROUND capability.
+        mPermissionMonitor.startMonitoring();
         mProxyTracker.loadGlobalProxy();
         registerNetdEventCallback();
         mTethering.systemReady();
@@ -2205,8 +2211,6 @@
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_ALWAYS_ON_NETWORKS));
 
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_SYSTEM_READY));
-
-        mPermissionMonitor.startMonitoring();
     }
 
     /**
@@ -3050,7 +3054,8 @@
     }
 
     private void handleAsyncChannelHalfConnect(Message msg) {
-        AsyncChannel ac = (AsyncChannel) msg.obj;
+        ensureRunningOnConnectivityServiceThread();
+        final AsyncChannel ac = (AsyncChannel) msg.obj;
         if (mNetworkFactoryInfos.containsKey(msg.replyTo)) {
             if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
                 if (VDBG) log("NetworkFactory connected");
@@ -3060,7 +3065,8 @@
                 // A network factory has connected.  Send it all current NetworkRequests.
                 for (NetworkRequestInfo nri : mNetworkRequests.values()) {
                     if (nri.request.isListen()) continue;
-                    NetworkAgentInfo nai = getNetworkForRequest(nri.request.requestId);
+                    ensureRunningOnConnectivityServiceThread();
+                    NetworkAgentInfo nai = nri.mSatisfier;
                     final int score;
                     final int serial;
                     if (nai != null) {
@@ -3116,6 +3122,7 @@
     // ConnectivityService, free its interfaces and clean up.
     // Must be called on the Handler thread.
     private void disconnectAndDestroyNetwork(NetworkAgentInfo nai) {
+        ensureRunningOnConnectivityServiceThread();
         if (DBG) {
             log(nai.name() + " got DISCONNECTED, was satisfying " + nai.numNetworkRequests());
         }
@@ -3162,14 +3169,17 @@
         // Remove all previously satisfied requests.
         for (int i = 0; i < nai.numNetworkRequests(); i++) {
             NetworkRequest request = nai.requestAt(i);
-            NetworkAgentInfo currentNetwork = getNetworkForRequest(request.requestId);
+            final NetworkRequestInfo nri = mNetworkRequests.get(request);
+            ensureRunningOnConnectivityServiceThread();
+            final NetworkAgentInfo currentNetwork = nri.mSatisfier;
             if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) {
-                clearNetworkForRequest(request.requestId);
+                nri.mSatisfier = null;
                 sendUpdatedScoreToFactories(request, null);
             }
         }
         nai.clearLingerState();
         if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
+            mDefaultNetworkNai = null;
             updateDataActivityTracking(null /* newNetwork */, nai);
             notifyLockdownVpn(nai);
             ensureNetworkTransitionWakelock(nai.name());
@@ -3178,7 +3188,7 @@
         if (!nai.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
             updateAllVpnsCapabilities();
         }
-        rematchAllNetworksAndRequests(null, 0);
+        rematchAllNetworksAndRequests();
         mLingerMonitor.noteDisconnect(nai);
         if (nai.created) {
             // Tell netd to clean up the configuration for this network
@@ -3253,6 +3263,7 @@
     }
 
     private void handleRegisterNetworkRequest(NetworkRequestInfo nri) {
+        ensureRunningOnConnectivityServiceThread();
         mNetworkRequests.put(nri.request, nri);
         mNetworkRequestInfoLogs.log("REGISTER " + nri);
         if (nri.request.isListen()) {
@@ -3263,8 +3274,8 @@
                 }
             }
         }
-        rematchAllNetworksAndRequests(null, 0);
-        if (nri.request.isRequest() && getNetworkForRequest(nri.request.requestId) == null) {
+        rematchAllNetworksAndRequests();
+        if (nri.request.isRequest() && nri.mSatisfier == null) {
             sendUpdatedScoreToFactories(nri.request, null);
         }
     }
@@ -3286,6 +3297,7 @@
     // - UnneededFor.LINGER: foreground NetworkRequests. If a network is unneeded for this reason,
     //   then it should be lingered.
     private boolean unneeded(NetworkAgentInfo nai, UnneededFor reason) {
+        ensureRunningOnConnectivityServiceThread();
         final int numRequests;
         switch (reason) {
             case TEARDOWN:
@@ -3310,6 +3322,7 @@
 
             // If this Network is already the highest scoring Network for a request, or if
             // there is hope for it to become one if it validated, then it is needed.
+            ensureRunningOnConnectivityServiceThread();
             if (nri.request.isRequest() && nai.satisfies(nri.request) &&
                     (nai.isSatisfyingRequest(nri.request.requestId) ||
                     // Note that this catches two important cases:
@@ -3319,8 +3332,8 @@
                     // 2. Unvalidated WiFi will not be reaped when validated cellular
                     //    is currently satisfying the request.  This is desirable when
                     //    WiFi ends up validating and out scoring cellular.
-                    getNetworkForRequest(nri.request.requestId).getCurrentScore() <
-                            nai.getCurrentScoreAsValidated())) {
+                    nri.mSatisfier.getCurrentScore()
+                            < nai.getCurrentScoreAsValidated())) {
                 return false;
             }
         }
@@ -3344,10 +3357,12 @@
     }
 
     private void handleTimedOutNetworkRequest(final NetworkRequestInfo nri) {
+        ensureRunningOnConnectivityServiceThread();
         if (mNetworkRequests.get(nri.request) == null) {
             return;
         }
-        if (getNetworkForRequest(nri.request.requestId) != null) {
+        ensureRunningOnConnectivityServiceThread();
+        if (nri.mSatisfier != null) {
             return;
         }
         if (VDBG || (DBG && nri.request.isRequest())) {
@@ -3374,6 +3389,8 @@
     }
 
     private void handleRemoveNetworkRequest(final NetworkRequestInfo nri) {
+        ensureRunningOnConnectivityServiceThread();
+
         nri.unlinkDeathRecipient();
         mNetworkRequests.remove(nri.request);
 
@@ -3393,7 +3410,8 @@
         mNetworkRequestInfoLogs.log("RELEASE " + nri);
         if (nri.request.isRequest()) {
             boolean wasKept = false;
-            NetworkAgentInfo nai = getNetworkForRequest(nri.request.requestId);
+            ensureRunningOnConnectivityServiceThread();
+            final NetworkAgentInfo nai = nri.mSatisfier;
             if (nai != null) {
                 boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
                 nai.removeRequest(nri.request.requestId);
@@ -3410,7 +3428,7 @@
                 } else {
                     wasKept = true;
                 }
-                clearNetworkForRequest(nri.request.requestId);
+                nri.mSatisfier = null;
                 if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) {
                     // Went from foreground to background.
                     updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
@@ -3499,13 +3517,12 @@
         }
 
         if (accept != nai.networkMisc.acceptUnvalidated) {
-            int oldScore = nai.getCurrentScore();
             nai.networkMisc.acceptUnvalidated = accept;
             // If network becomes partial connectivity and user already accepted to use this
             // network, we should respect the user's option and don't need to popup the
             // PARTIAL_CONNECTIVITY notification to user again.
             nai.networkMisc.acceptPartialConnectivity = accept;
-            rematchAllNetworksAndRequests(nai, oldScore);
+            rematchAllNetworksAndRequests();
             sendUpdatedScoreToFactories(nai);
         }
 
@@ -3574,9 +3591,8 @@
             return;
         }
         if (!nai.avoidUnvalidated) {
-            int oldScore = nai.getCurrentScore();
             nai.avoidUnvalidated = true;
-            rematchAllNetworksAndRequests(nai, oldScore);
+            rematchAllNetworksAndRequests();
             sendUpdatedScoreToFactories(nai);
         }
     }
@@ -3677,7 +3693,7 @@
 
 
     private void rematchForAvoidBadWifiUpdate() {
-        rematchAllNetworksAndRequests(null, 0);
+        rematchAllNetworksAndRequests();
         for (NetworkAgentInfo nai: mNetworkAgentInfos.values()) {
             if (nai.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
                 sendUpdatedScoreToFactories(nai);
@@ -5093,6 +5109,11 @@
      */
     private class NetworkRequestInfo implements IBinder.DeathRecipient {
         final NetworkRequest request;
+        // The network currently satisfying this request, or null if none. Must only be touched
+        // on the handler thread. This only makes sense for network requests and not for listens,
+        // as defined by NetworkRequest#isRequest(). For listens, this is always null.
+        @Nullable
+        NetworkAgentInfo mSatisfier;
         final PendingIntent mPendingIntent;
         boolean mPendingIntentSent;
         private final IBinder mBinder;
@@ -5479,16 +5500,6 @@
         if (DBG) log("unregisterNetworkFactory for " + nfi.name);
     }
 
-    /**
-     * NetworkAgentInfo supporting a request by requestId.
-     * These have already been vetted (their Capabilities satisfy the request)
-     * and the are the highest scored network available.
-     * the are keyed off the Requests requestId.
-     */
-    // NOTE: Accessed on multiple threads, must be synchronized on itself.
-    @GuardedBy("mNetworkForRequestId")
-    private final SparseArray<NetworkAgentInfo> mNetworkForRequestId = new SparseArray<>();
-
     // NOTE: Accessed on multiple threads, must be synchronized on itself.
     @GuardedBy("mNetworkForNetId")
     private final SparseArray<NetworkAgentInfo> mNetworkForNetId = new SparseArray<>();
@@ -5506,7 +5517,11 @@
     private final HashSet<Integer> mBlockedAppUids = new HashSet<>();
 
     // Note: if mDefaultRequest is changed, NetworkMonitor needs to be updated.
+    @NonNull
     private final NetworkRequest mDefaultRequest;
+    // The NetworkAgentInfo currently satisfying the default request, if any.
+    @Nullable
+    private volatile NetworkAgentInfo mDefaultNetworkNai = null;
 
     // Request used to optionally keep mobile data active even when higher
     // priority networks like Wi-Fi are active.
@@ -5516,26 +5531,8 @@
     // priority networks like ethernet are active.
     private final NetworkRequest mDefaultWifiRequest;
 
-    private NetworkAgentInfo getNetworkForRequest(int requestId) {
-        synchronized (mNetworkForRequestId) {
-            return mNetworkForRequestId.get(requestId);
-        }
-    }
-
-    private void clearNetworkForRequest(int requestId) {
-        synchronized (mNetworkForRequestId) {
-            mNetworkForRequestId.remove(requestId);
-        }
-    }
-
-    private void setNetworkForRequest(int requestId, NetworkAgentInfo nai) {
-        synchronized (mNetworkForRequestId) {
-            mNetworkForRequestId.put(requestId, nai);
-        }
-    }
-
     private NetworkAgentInfo getDefaultNetwork() {
-        return getNetworkForRequest(mDefaultRequest.requestId);
+        return mDefaultNetworkNai;
     }
 
     @Nullable
@@ -5968,7 +5965,7 @@
         } else {
             // If the requestable capabilities have changed or the score changed, we can't have been
             // called by rematchNetworkAndRequests, so it's safe to start a rematch.
-            rematchAllNetworksAndRequests(nai, oldScore);
+            rematchAllNetworksAndRequests();
             notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
         }
 
@@ -6253,7 +6250,7 @@
         }
     }
 
-    private void makeDefault(NetworkAgentInfo newNetwork) {
+    private void makeDefault(@NonNull final NetworkAgentInfo newNetwork) {
         if (DBG) log("Switching to new default network: " + newNetwork);
 
         try {
@@ -6262,6 +6259,7 @@
             loge("Exception setting default network :" + e);
         }
 
+        mDefaultNetworkNai = newNetwork;
         notifyLockdownVpn(newNetwork);
         handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
         updateTcpBufferSizes(newNetwork.linkProperties.getTcpBufferSizes());
@@ -6296,6 +6294,41 @@
         }
     }
 
+    private ArrayMap<NetworkRequestInfo, NetworkAgentInfo> computeRequestReassignmentForNetwork(
+            @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;
+
+            final NetworkAgentInfo currentNetwork = nri.mSatisfier;
+            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);
+                }
+            } else if (newNetwork.isSatisfyingRequest(nri.request.requestId)) {
+                reassignedRequests.put(nri, null);
+            }
+        }
+        return reassignedRequests;
+    }
+
     // Handles a network appearing or improving its score.
     //
     // - Evaluates all current NetworkRequests that can be
@@ -6309,10 +6342,6 @@
     // - Tears down newNetwork if it just became validated
     //   but turns out to be unneeded.
     //
-    // - If reapUnvalidatedNetworks==REAP, tears down unvalidated
-    //   networks that have no chance (i.e. even if validated)
-    //   of becoming the highest scoring network.
-    //
     // NOTE: This function only adds NetworkRequests that "newNetwork" could satisfy,
     // it does not remove NetworkRequests that other Networks could better satisfy.
     // If you need to handle decreases in score, use {@link rematchAllNetworksAndRequests}.
@@ -6320,11 +6349,9 @@
     // as it performs better by a factor of the number of Networks.
     //
     // @param newNetwork is the network to be matched against NetworkRequests.
-    // @param reapUnvalidatedNetworks indicates if an additional pass over all networks should be
-    //               performed to tear down unvalidated networks that have no chance (i.e. even if
-    //               validated) of becoming the highest scoring network.
-    private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork,
-            ReapUnvalidatedNetworks reapUnvalidatedNetworks, long now) {
+    // @param now the time the rematch starts, as returned by SystemClock.elapsedRealtime();
+    private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork, long now) {
+        ensureRunningOnConnectivityServiceThread();
         if (!newNetwork.everConnected) return;
         boolean keep = newNetwork.isVPN();
         boolean isNewDefault = false;
@@ -6335,74 +6362,54 @@
 
         if (VDBG || DDBG) log("rematching " + newNetwork.name());
 
-        // Find and migrate to this Network any NetworkRequests for
-        // which this network is now the best.
-        ArrayList<NetworkAgentInfo> affectedNetworks = new ArrayList<>();
-        ArrayList<NetworkRequestInfo> addedRequests = new ArrayList<>();
+        final ArrayMap<NetworkRequestInfo, NetworkAgentInfo> reassignedRequests =
+                computeRequestReassignmentForNetwork(newNetwork);
+
         NetworkCapabilities nc = newNetwork.networkCapabilities;
         if (VDBG) log(" network has: " + nc);
-        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;
 
-            final NetworkAgentInfo currentNetwork = getNetworkForRequest(nri.request.requestId);
-            final boolean satisfies = newNetwork.satisfies(nri.request);
-            if (newNetwork == currentNetwork && satisfies) {
-                if (VDBG) {
-                    log("Network " + newNetwork.name() + " was already satisfying" +
-                            " request " + nri.request.requestId + ". No change.");
+        // Find and migrate to this Network any NetworkRequests for
+        // which this network is now the best.
+        final ArrayList<NetworkAgentInfo> removedRequests = new ArrayList<>();
+        final ArrayList<NetworkRequestInfo> addedRequests = new ArrayList<>();
+        for (final Map.Entry<NetworkRequestInfo, NetworkAgentInfo> entry :
+                reassignedRequests.entrySet()) {
+            final NetworkRequestInfo nri = entry.getKey();
+            final NetworkAgentInfo previousSatisfier = nri.mSatisfier;
+            final NetworkAgentInfo newSatisfier = entry.getValue();
+            if (newSatisfier != null) {
+                if (VDBG) log("rematch for " + newSatisfier.name());
+                if (previousSatisfier != null) {
+                    if (VDBG || DDBG) {
+                        log("   accepting network in place of " + previousSatisfier.name());
+                    }
+                    previousSatisfier.removeRequest(nri.request.requestId);
+                    previousSatisfier.lingerRequest(nri.request, now, mLingerDelayMs);
+                    removedRequests.add(previousSatisfier);
+                } else {
+                    if (VDBG || DDBG) log("   accepting network in place of null");
                 }
+                newSatisfier.unlingerRequest(nri.request);
+                nri.mSatisfier = newSatisfier;
+                if (!newSatisfier.addRequest(nri.request)) {
+                    Slog.wtf(TAG, "BUG: " + newSatisfier.name() + " already has " + nri.request);
+                }
+                addedRequests.add(nri);
                 keep = true;
-                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) {
-                    if (VDBG) log("rematch for " + newNetwork.name());
-                    if (currentNetwork != null) {
-                        if (VDBG || DDBG){
-                            log("   accepting network in place of " + currentNetwork.name());
-                        }
-                        currentNetwork.removeRequest(nri.request.requestId);
-                        currentNetwork.lingerRequest(nri.request, now, mLingerDelayMs);
-                        affectedNetworks.add(currentNetwork);
-                    } else {
-                        if (VDBG || DDBG) log("   accepting network in place of null");
-                    }
-                    newNetwork.unlingerRequest(nri.request);
-                    setNetworkForRequest(nri.request.requestId, newNetwork);
-                    if (!newNetwork.addRequest(nri.request)) {
-                        Slog.wtf(TAG, "BUG: " + newNetwork.name() + " already has " + nri.request);
-                    }
-                    addedRequests.add(nri);
-                    keep = true;
-                    // Tell NetworkFactories about the new score, so they can stop
-                    // trying to connect if they know they cannot match it.
-                    // TODO - this could get expensive if we have a lot of requests for this
-                    // network.  Think about if there is a way to reduce this.  Push
-                    // netid->request mapping to each factory?
-                    sendUpdatedScoreToFactories(nri.request, newNetwork);
-                    if (isDefaultRequest(nri)) {
-                        isNewDefault = true;
-                        oldDefaultNetwork = currentNetwork;
-                        if (currentNetwork != null) {
-                            mLingerMonitor.noteLingerDefaultNetwork(currentNetwork, newNetwork);
-                        }
+                // Tell NetworkFactories about the new score, so they can stop
+                // trying to connect if they know they cannot match it.
+                // TODO - this could get expensive if we have a lot of requests for this
+                // network.  Think about if there is a way to reduce this.  Push
+                // netid->request mapping to each factory?
+                sendUpdatedScoreToFactories(nri.request, newSatisfier);
+                if (isDefaultRequest(nri)) {
+                    isNewDefault = true;
+                    oldDefaultNetwork = previousSatisfier;
+                    if (previousSatisfier != null) {
+                        mLingerMonitor.noteLingerDefaultNetwork(previousSatisfier, newSatisfier);
                     }
                 }
-            } else if (newNetwork.isSatisfyingRequest(nri.request.requestId)) {
+            } else {
                 // If "newNetwork" is listed as satisfying "nri" but no longer satisfies "nri",
                 // mark it as no longer satisfying "nri".  Because networks are processed by
                 // rematchAllNetworksAndRequests() in descending score order, "currentNetwork" will
@@ -6415,13 +6422,14 @@
                             " request " + nri.request.requestId);
                 }
                 newNetwork.removeRequest(nri.request.requestId);
-                if (currentNetwork == newNetwork) {
-                    clearNetworkForRequest(nri.request.requestId);
+                if (previousSatisfier == newNetwork) {
+                    nri.mSatisfier = null;
+                    if (isDefaultRequest(nri)) mDefaultNetworkNai = null;
                     sendUpdatedScoreToFactories(nri.request, null);
                 } else {
                     Slog.wtf(TAG, "BUG: Removing request " + nri.request.requestId + " from " +
                             newNetwork.name() +
-                            " without updating mNetworkForRequestId or factories!");
+                            " without updating mSatisfier or factories!");
                 }
                 // TODO: Technically, sending CALLBACK_LOST here is
                 // incorrect if there is a replacement network currently
@@ -6433,6 +6441,7 @@
                 callCallbackForRequest(nri, newNetwork, ConnectivityManager.CALLBACK_LOST, 0);
             }
         }
+
         if (isNewDefault) {
             updateDataActivityTracking(newNetwork, oldDefaultNetwork);
             // Notify system services that this network is up.
@@ -6472,7 +6481,7 @@
 
         // Linger any networks that are no longer needed. This should be done after sending the
         // available callback for newNetwork.
-        for (NetworkAgentInfo nai : affectedNetworks) {
+        for (NetworkAgentInfo nai : removedRequests) {
             updateLingerState(nai, now);
         }
         // Possibly unlinger newNetwork. Unlingering a network does not send any callbacks so it
@@ -6540,66 +6549,43 @@
                 mLegacyTypeTracker.add(TYPE_VPN, newNetwork);
             }
         }
-        if (reapUnvalidatedNetworks == ReapUnvalidatedNetworks.REAP) {
-            for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
-                if (unneeded(nai, UnneededFor.TEARDOWN)) {
-                    if (nai.getLingerExpiry() > 0) {
-                        // This network has active linger timers and no requests, but is not
-                        // lingering. Linger it.
-                        //
-                        // One way (the only way?) this can happen if this network is unvalidated
-                        // and became unneeded due to another network improving its score to the
-                        // point where this network will no longer be able to satisfy any requests
-                        // even if it validates.
-                        updateLingerState(nai, now);
-                    } else {
-                        if (DBG) log("Reaping " + nai.name());
-                        teardownUnneededNetwork(nai);
-                    }
-                }
-            }
-        }
     }
 
     /**
      * Attempt to rematch all Networks with NetworkRequests.  This may result in Networks
      * being disconnected.
-     * @param changed If only one Network's score or capabilities have been modified since the last
-     *         time this function was called, pass this Network in this argument, otherwise pass
-     *         null.
-     * @param oldScore If only one Network has been changed but its NetworkCapabilities have not
-     *         changed, pass in the Network's score (from getCurrentScore()) prior to the change via
-     *         this argument, otherwise pass {@code changed.getCurrentScore()} or 0 if
-     *         {@code changed} is {@code null}. This is because NetworkCapabilities influence a
-     *         network's score.
      */
-    private void rematchAllNetworksAndRequests(NetworkAgentInfo changed, int oldScore) {
-        // TODO: This may get slow.  The "changed" parameter is provided for future optimization
-        // to avoid the slowness.  It is not simply enough to process just "changed", for
-        // example in the case where "changed"'s score decreases and another network should begin
-        // satisfying a NetworkRequest that "changed" currently satisfies.
-
-        // Optimization: Only reprocess "changed" if its score improved.  This is safe because it
-        // can only add more NetworkRequests satisfied by "changed", and this is exactly what
-        // rematchNetworkAndRequests() handles.
+    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.
         final long now = SystemClock.elapsedRealtime();
-        if (changed != null && oldScore < changed.getCurrentScore()) {
-            rematchNetworkAndRequests(changed, ReapUnvalidatedNetworks.REAP, now);
-        } else {
-            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 and inadvertently unlinger networks.
-            Arrays.sort(nais);
-            for (NetworkAgentInfo nai : nais) {
-                rematchNetworkAndRequests(nai,
-                        // Only reap the last time through the loop.  Reaping before all rematching
-                        // is complete could incorrectly teardown a network that hasn't yet been
-                        // rematched.
-                        (nai != nais[nais.length-1]) ? ReapUnvalidatedNetworks.DONT_REAP
-                                : ReapUnvalidatedNetworks.REAP,
-                        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 and inadvertently unlinger networks.
+        Arrays.sort(nais);
+        for (NetworkAgentInfo nai : nais) {
+            rematchNetworkAndRequests(nai, now);
+        }
+        for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+            if (unneeded(nai, UnneededFor.TEARDOWN)) {
+                if (nai.getLingerExpiry() > 0) {
+                    // This network has active linger timers and no requests, but is not
+                    // lingering. Linger it.
+                    //
+                    // One way (the only way?) this can happen if this network is unvalidated
+                    // and became unneeded due to another network improving its score to the
+                    // point where this network will no longer be able to satisfy any requests
+                    // even if it validates.
+                    updateLingerState(nai, now);
+                } else {
+                    if (DBG) log("Reaping " + nai.name());
+                    teardownUnneededNetwork(nai);
+                }
             }
         }
     }
@@ -6698,8 +6684,7 @@
             }
 
             // Consider network even though it is not yet validated.
-            final long now = SystemClock.elapsedRealtime();
-            rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP, now);
+            rematchAllNetworksAndRequests();
 
             // This has to happen after matching the requests, because callbacks are just requests.
             notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
@@ -6720,7 +6705,7 @@
                 state == NetworkInfo.State.SUSPENDED)) {
             // going into or coming out of SUSPEND: re-score and notify
             if (networkAgent.getCurrentScore() != oldScore) {
-                rematchAllNetworksAndRequests(networkAgent, oldScore);
+                rematchAllNetworksAndRequests();
             }
             updateCapabilities(networkAgent.getCurrentScore(), networkAgent,
                     networkAgent.networkCapabilities);
@@ -6734,19 +6719,9 @@
     }
 
     private void updateNetworkScore(NetworkAgentInfo nai, NetworkScore ns) {
-        int score = ns.getIntExtension(NetworkScore.LEGACY_SCORE);
-        if (VDBG || DDBG) log("updateNetworkScore for " + nai.name() + " to " + score);
-        if (score < 0) {
-            loge("updateNetworkScore for " + nai.name() + " got a negative score (" + score +
-                    ").  Bumping score to min of 0");
-            score = 0;
-        }
-
-        final int oldScore = nai.getCurrentScore();
+        if (VDBG || DDBG) log("updateNetworkScore for " + nai.name() + " to " + ns);
         nai.setNetworkScore(ns);
-
-        rematchAllNetworksAndRequests(nai, oldScore);
-
+        rematchAllNetworksAndRequests();
         sendUpdatedScoreToFactories(nai);
     }
 
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 61f37fd..aad2f3d 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -20,6 +20,9 @@
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
 import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_SUPL;
+import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
+import static android.net.ConnectivityManager.EXTRA_NETWORK_TYPE;
 import static android.net.ConnectivityManager.NETID_UNSET;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
@@ -28,6 +31,7 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
 import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK;
@@ -145,6 +149,7 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkFactory;
+import android.net.NetworkInfo;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
@@ -244,6 +249,7 @@
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Predicate;
 
 import kotlin.reflect.KClass;
 
@@ -330,6 +336,9 @@
 
     private class MockContext extends BroadcastInterceptingContext {
         private final MockContentResolver mContentResolver;
+        // Contains all registered receivers since this object was created. Useful to clear
+        // them when needed, as BroadcastInterceptingContext does not provide this facility.
+        private final List<BroadcastReceiver> mRegisteredReceivers = new ArrayList<>();
 
         @Spy private Resources mResources;
         private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>();
@@ -343,6 +352,7 @@
                             "wifi,1,1,1,-1,true",
                             "mobile,0,0,0,-1,true",
                             "mobile_mms,2,0,2,60000,true",
+                            "mobile_supl,3,0,2,60000,true",
                     });
 
             when(mResources.getStringArray(
@@ -410,6 +420,19 @@
             // make sure the code does not rely on unexpected permissions.
             super.enforceCallingOrSelfPermission(permission, message);
         }
+
+        @Override
+        public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+            mRegisteredReceivers.add(receiver);
+            return super.registerReceiver(receiver, filter);
+        }
+
+        public void clearRegisteredReceivers() {
+            // super.unregisterReceiver is a no-op for receivers that are not registered (because
+            // they haven't been registered or because they have already been unregistered).
+            // For the same reason, don't bother clearing mRegisteredReceivers.
+            for (final BroadcastReceiver rcv : mRegisteredReceivers) unregisterReceiver(rcv);
+        }
     }
 
     private void waitForIdle() {
@@ -438,7 +461,7 @@
         }
 
         // Bring up a network that we can use to send messages to ConnectivityService.
-        ConditionVariable cv = waitForConnectivityBroadcasts(1);
+        ConditionVariable cv = registerConnectivityBroadcast(1);
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
         waitFor(cv);
@@ -458,7 +481,7 @@
     @Ignore
     public void verifyThatNotWaitingForIdleCausesRaceConditions() throws Exception {
         // Bring up a network that we can use to send messages to ConnectivityService.
-        ConditionVariable cv = waitForConnectivityBroadcasts(1);
+        ConditionVariable cv = registerConnectivityBroadcast(1);
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
         waitFor(cv);
@@ -1227,17 +1250,26 @@
      * Return a ConditionVariable that opens when {@code count} numbers of CONNECTIVITY_ACTION
      * broadcasts are received.
      */
-    private ConditionVariable waitForConnectivityBroadcasts(final int count) {
+    private ConditionVariable registerConnectivityBroadcast(final int count) {
+        return registerConnectivityBroadcastThat(count, intent -> true);
+    }
+
+    private ConditionVariable registerConnectivityBroadcastThat(final int count,
+            @NonNull final Predicate<Intent> filter) {
         final ConditionVariable cv = new ConditionVariable();
-        mServiceContext.registerReceiver(new BroadcastReceiver() {
+        final IntentFilter intentFilter = new IntentFilter(CONNECTIVITY_ACTION);
+        intentFilter.addAction(CONNECTIVITY_ACTION_SUPL);
+        final BroadcastReceiver receiver = new BroadcastReceiver() {
                     private int remaining = count;
                     public void onReceive(Context context, Intent intent) {
+                        if (!filter.test(intent)) return;
                         if (--remaining == 0) {
                             cv.open();
                             mServiceContext.unregisterReceiver(this);
                         }
                     }
-                }, new IntentFilter(CONNECTIVITY_ACTION));
+                };
+        mServiceContext.registerReceiver(receiver, intentFilter);
         return cv;
     }
 
@@ -1258,6 +1290,75 @@
     }
 
     @Test
+    public void testNetworkFeature() throws Exception {
+        // Connect the cell agent and wait for the connected broadcast.
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.addCapability(NET_CAPABILITY_SUPL);
+        final ConditionVariable cv1 = registerConnectivityBroadcastThat(1,
+                intent -> intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) == TYPE_MOBILE);
+        mCellNetworkAgent.connect(true);
+        waitFor(cv1);
+
+        // Build legacy request for SUPL.
+        final NetworkCapabilities legacyCaps = new NetworkCapabilities();
+        legacyCaps.addTransportType(TRANSPORT_CELLULAR);
+        legacyCaps.addCapability(NET_CAPABILITY_SUPL);
+        final NetworkRequest legacyRequest = new NetworkRequest(legacyCaps, TYPE_MOBILE_SUPL,
+                ConnectivityManager.REQUEST_ID_UNSET, NetworkRequest.Type.REQUEST);
+
+        // Send request and check that the legacy broadcast for SUPL is sent correctly.
+        final TestNetworkCallback callback = new TestNetworkCallback();
+        final ConditionVariable cv2 = registerConnectivityBroadcastThat(1,
+                intent -> intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) == TYPE_MOBILE_SUPL);
+        mCm.requestNetwork(legacyRequest, callback);
+        callback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
+        waitFor(cv2);
+
+        // File another request, withdraw it and make sure no broadcast is sent
+        final ConditionVariable cv3 = registerConnectivityBroadcast(1);
+        final TestNetworkCallback callback2 = new TestNetworkCallback();
+        mCm.requestNetwork(legacyRequest, callback2);
+        callback2.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
+        mCm.unregisterNetworkCallback(callback2);
+        assertFalse(cv3.block(800)); // 800ms long enough to at least flake if this is sent
+        // As the broadcast did not fire, the receiver was not unregistered. Do this now.
+        mServiceContext.clearRegisteredReceivers();
+
+        // Withdraw the request and check that the broadcast for disconnection is sent.
+        final ConditionVariable cv4 = registerConnectivityBroadcastThat(1, intent ->
+                !((NetworkInfo) intent.getExtra(EXTRA_NETWORK_INFO, -1)).isConnected()
+                        && intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) == TYPE_MOBILE_SUPL);
+        mCm.unregisterNetworkCallback(callback);
+        waitFor(cv4);
+
+        // Re-file the request and expect the connected broadcast again
+        final ConditionVariable cv5 = registerConnectivityBroadcastThat(1,
+                intent -> intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) == TYPE_MOBILE_SUPL);
+        final TestNetworkCallback callback3 = new TestNetworkCallback();
+        mCm.requestNetwork(legacyRequest, callback3);
+        callback3.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
+        waitFor(cv5);
+
+        // Disconnect the network and expect two disconnected broadcasts, one for SUPL and one
+        // for mobile. Use a small hack to check that both have been sent, but the order is
+        // not contractual.
+        final AtomicBoolean vanillaAction = new AtomicBoolean(false);
+        final AtomicBoolean suplAction = new AtomicBoolean(false);
+        final ConditionVariable cv6 = registerConnectivityBroadcastThat(2, intent -> {
+            if (intent.getAction().equals(CONNECTIVITY_ACTION)) {
+                vanillaAction.set(true);
+            } else if (intent.getAction().equals(CONNECTIVITY_ACTION_SUPL)) {
+                suplAction.set(true);
+            }
+            return !((NetworkInfo) intent.getExtra(EXTRA_NETWORK_INFO, -1)).isConnected();
+        });
+        mCellNetworkAgent.disconnect();
+        waitFor(cv6);
+        assertTrue(vanillaAction.get());
+        assertTrue(suplAction.get());
+    }
+
+    @Test
     public void testLingering() throws Exception {
         verifyNoNetwork();
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
@@ -1265,7 +1366,7 @@
         assertNull(mCm.getActiveNetworkInfo());
         assertNull(mCm.getActiveNetwork());
         // Test bringing up validated cellular.
-        ConditionVariable cv = waitForConnectivityBroadcasts(1);
+        ConditionVariable cv = registerConnectivityBroadcast(1);
         mCellNetworkAgent.connect(true);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_CELLULAR);
@@ -1275,7 +1376,7 @@
         assertTrue(mCm.getAllNetworks()[0].equals(mWiFiNetworkAgent.getNetwork()) ||
                 mCm.getAllNetworks()[1].equals(mWiFiNetworkAgent.getNetwork()));
         // Test bringing up validated WiFi.
-        cv = waitForConnectivityBroadcasts(2);
+        cv = registerConnectivityBroadcast(2);
         mWiFiNetworkAgent.connect(true);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_WIFI);
@@ -1292,7 +1393,7 @@
         assertLength(1, mCm.getAllNetworks());
         assertEquals(mCm.getAllNetworks()[0], mCm.getActiveNetwork());
         // Test WiFi disconnect.
-        cv = waitForConnectivityBroadcasts(1);
+        cv = registerConnectivityBroadcast(1);
         mWiFiNetworkAgent.disconnect();
         waitFor(cv);
         verifyNoNetwork();
@@ -1302,7 +1403,7 @@
     public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception {
         // Test bringing up unvalidated WiFi
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
-        ConditionVariable cv = waitForConnectivityBroadcasts(1);
+        ConditionVariable cv = registerConnectivityBroadcast(1);
         mWiFiNetworkAgent.connect(false);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_WIFI);
@@ -1317,17 +1418,17 @@
         verifyActiveNetwork(TRANSPORT_WIFI);
         // Test bringing up validated cellular
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
-        cv = waitForConnectivityBroadcasts(2);
+        cv = registerConnectivityBroadcast(2);
         mCellNetworkAgent.connect(true);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_CELLULAR);
         // Test cellular disconnect.
-        cv = waitForConnectivityBroadcasts(2);
+        cv = registerConnectivityBroadcast(2);
         mCellNetworkAgent.disconnect();
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_WIFI);
         // Test WiFi disconnect.
-        cv = waitForConnectivityBroadcasts(1);
+        cv = registerConnectivityBroadcast(1);
         mWiFiNetworkAgent.disconnect();
         waitFor(cv);
         verifyNoNetwork();
@@ -1337,23 +1438,23 @@
     public void testUnvalidatedWifiOutscoresUnvalidatedCellular() throws Exception {
         // Test bringing up unvalidated cellular.
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
-        ConditionVariable cv = waitForConnectivityBroadcasts(1);
+        ConditionVariable cv = registerConnectivityBroadcast(1);
         mCellNetworkAgent.connect(false);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_CELLULAR);
         // Test bringing up unvalidated WiFi.
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
-        cv = waitForConnectivityBroadcasts(2);
+        cv = registerConnectivityBroadcast(2);
         mWiFiNetworkAgent.connect(false);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_WIFI);
         // Test WiFi disconnect.
-        cv = waitForConnectivityBroadcasts(2);
+        cv = registerConnectivityBroadcast(2);
         mWiFiNetworkAgent.disconnect();
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_CELLULAR);
         // Test cellular disconnect.
-        cv = waitForConnectivityBroadcasts(1);
+        cv = registerConnectivityBroadcast(1);
         mCellNetworkAgent.disconnect();
         waitFor(cv);
         verifyNoNetwork();
@@ -1363,7 +1464,7 @@
     public void testUnlingeringDoesNotValidate() throws Exception {
         // Test bringing up unvalidated WiFi.
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
-        ConditionVariable cv = waitForConnectivityBroadcasts(1);
+        ConditionVariable cv = registerConnectivityBroadcast(1);
         mWiFiNetworkAgent.connect(false);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_WIFI);
@@ -1371,14 +1472,14 @@
                 NET_CAPABILITY_VALIDATED));
         // Test bringing up validated cellular.
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
-        cv = waitForConnectivityBroadcasts(2);
+        cv = registerConnectivityBroadcast(2);
         mCellNetworkAgent.connect(true);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_CELLULAR);
         assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
                 NET_CAPABILITY_VALIDATED));
         // Test cellular disconnect.
-        cv = waitForConnectivityBroadcasts(2);
+        cv = registerConnectivityBroadcast(2);
         mCellNetworkAgent.disconnect();
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_WIFI);
@@ -1391,23 +1492,23 @@
     public void testCellularOutscoresWeakWifi() throws Exception {
         // Test bringing up validated cellular.
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
-        ConditionVariable cv = waitForConnectivityBroadcasts(1);
+        ConditionVariable cv = registerConnectivityBroadcast(1);
         mCellNetworkAgent.connect(true);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_CELLULAR);
         // Test bringing up validated WiFi.
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
-        cv = waitForConnectivityBroadcasts(2);
+        cv = registerConnectivityBroadcast(2);
         mWiFiNetworkAgent.connect(true);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_WIFI);
         // Test WiFi getting really weak.
-        cv = waitForConnectivityBroadcasts(2);
+        cv = registerConnectivityBroadcast(2);
         mWiFiNetworkAgent.adjustScore(-11);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_CELLULAR);
         // Test WiFi restoring signal strength.
-        cv = waitForConnectivityBroadcasts(2);
+        cv = registerConnectivityBroadcast(2);
         mWiFiNetworkAgent.adjustScore(11);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_WIFI);
@@ -1427,7 +1528,7 @@
         mCellNetworkAgent.expectDisconnected();
         // Test bringing up validated WiFi.
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
-        final ConditionVariable cv = waitForConnectivityBroadcasts(1);
+        final ConditionVariable cv = registerConnectivityBroadcast(1);
         mWiFiNetworkAgent.connect(true);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_WIFI);
@@ -1446,18 +1547,18 @@
     public void testCellularFallback() throws Exception {
         // Test bringing up validated cellular.
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
-        ConditionVariable cv = waitForConnectivityBroadcasts(1);
+        ConditionVariable cv = registerConnectivityBroadcast(1);
         mCellNetworkAgent.connect(true);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_CELLULAR);
         // Test bringing up validated WiFi.
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
-        cv = waitForConnectivityBroadcasts(2);
+        cv = registerConnectivityBroadcast(2);
         mWiFiNetworkAgent.connect(true);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_WIFI);
         // Reevaluate WiFi (it'll instantly fail DNS).
-        cv = waitForConnectivityBroadcasts(2);
+        cv = registerConnectivityBroadcast(2);
         assertTrue(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
                 NET_CAPABILITY_VALIDATED));
         mCm.reportBadNetwork(mWiFiNetworkAgent.getNetwork());
@@ -1467,7 +1568,7 @@
                 NET_CAPABILITY_VALIDATED));
         verifyActiveNetwork(TRANSPORT_CELLULAR);
         // Reevaluate cellular (it'll instantly fail DNS).
-        cv = waitForConnectivityBroadcasts(2);
+        cv = registerConnectivityBroadcast(2);
         assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability(
                 NET_CAPABILITY_VALIDATED));
         mCm.reportBadNetwork(mCellNetworkAgent.getNetwork());
@@ -1484,18 +1585,18 @@
     public void testWiFiFallback() throws Exception {
         // Test bringing up unvalidated WiFi.
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
-        ConditionVariable cv = waitForConnectivityBroadcasts(1);
+        ConditionVariable cv = registerConnectivityBroadcast(1);
         mWiFiNetworkAgent.connect(false);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_WIFI);
         // Test bringing up validated cellular.
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
-        cv = waitForConnectivityBroadcasts(2);
+        cv = registerConnectivityBroadcast(2);
         mCellNetworkAgent.connect(true);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_CELLULAR);
         // Reevaluate cellular (it'll instantly fail DNS).
-        cv = waitForConnectivityBroadcasts(2);
+        cv = registerConnectivityBroadcast(2);
         assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability(
                 NET_CAPABILITY_VALIDATED));
         mCm.reportBadNetwork(mCellNetworkAgent.getNetwork());
@@ -1570,7 +1671,7 @@
         mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
 
         // Test unvalidated networks
-        ConditionVariable cv = waitForConnectivityBroadcasts(1);
+        ConditionVariable cv = registerConnectivityBroadcast(1);
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(false);
         genericNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
@@ -1585,7 +1686,7 @@
         assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
-        cv = waitForConnectivityBroadcasts(2);
+        cv = registerConnectivityBroadcast(2);
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
         genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -1594,7 +1695,7 @@
         waitFor(cv);
         assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
 
-        cv = waitForConnectivityBroadcasts(2);
+        cv = registerConnectivityBroadcast(2);
         mWiFiNetworkAgent.disconnect();
         genericNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
         wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
@@ -1602,7 +1703,7 @@
         waitFor(cv);
         assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
 
-        cv = waitForConnectivityBroadcasts(1);
+        cv = registerConnectivityBroadcast(1);
         mCellNetworkAgent.disconnect();
         genericNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
         cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
@@ -2192,7 +2293,7 @@
 
         // Test bringing up validated WiFi.
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
-        final ConditionVariable cv = waitForConnectivityBroadcasts(1);
+        final ConditionVariable cv = registerConnectivityBroadcast(1);
         mWiFiNetworkAgent.connect(true);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_WIFI);
@@ -2220,7 +2321,7 @@
     public void testMMSonCell() throws Exception {
         // Test bringing up cellular without MMS
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
-        ConditionVariable cv = waitForConnectivityBroadcasts(1);
+        ConditionVariable cv = registerConnectivityBroadcast(1);
         mCellNetworkAgent.connect(false);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_CELLULAR);
@@ -3679,7 +3780,7 @@
         }
 
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
-        ConditionVariable cv = waitForConnectivityBroadcasts(1);
+        ConditionVariable cv = registerConnectivityBroadcast(1);
         mWiFiNetworkAgent.connect(true);
         waitFor(cv);
         verifyActiveNetwork(TRANSPORT_WIFI);
@@ -4212,7 +4313,7 @@
         assertNotPinnedToWifi();
 
         // Disconnect cell and wifi.
-        ConditionVariable cv = waitForConnectivityBroadcasts(3);  // cell down, wifi up, wifi down.
+        ConditionVariable cv = registerConnectivityBroadcast(3);  // cell down, wifi up, wifi down.
         mCellNetworkAgent.disconnect();
         mWiFiNetworkAgent.disconnect();
         waitFor(cv);
@@ -4225,7 +4326,7 @@
         assertPinnedToWifiWithWifiDefault();
 
         // ... and is maintained even when that network is no longer the default.
-        cv = waitForConnectivityBroadcasts(1);
+        cv = registerConnectivityBroadcast(1);
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mCellNetworkAgent.connect(true);
         waitFor(cv);
@@ -4326,7 +4427,7 @@
 
     @Test
     public void testNetworkInfoOfTypeNone() throws Exception {
-        ConditionVariable broadcastCV = waitForConnectivityBroadcasts(1);
+        ConditionVariable broadcastCV = registerConnectivityBroadcast(1);
 
         verifyNoNetwork();
         TestNetworkAgentWrapper wifiAware = new TestNetworkAgentWrapper(TRANSPORT_WIFI_AWARE);
@@ -5778,7 +5879,7 @@
                 .destroyNetworkCache(eq(mCellNetworkAgent.getNetwork().netId));
 
         // Disconnect wifi
-        ConditionVariable cv = waitForConnectivityBroadcasts(1);
+        ConditionVariable cv = registerConnectivityBroadcast(1);
         reset(mNetworkManagementService);
         mWiFiNetworkAgent.disconnect();
         waitFor(cv);
diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
index f045369..42d4cf3 100644
--- a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
+++ b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
@@ -18,6 +18,7 @@
 
 import android.net.ConnectivityManager.TYPE_ETHERNET
 import android.net.ConnectivityManager.TYPE_MOBILE
+import android.net.ConnectivityManager.TYPE_MOBILE_SUPL
 import android.net.ConnectivityManager.TYPE_WIFI
 import android.net.ConnectivityManager.TYPE_WIMAX
 import android.net.NetworkInfo.DetailedState.CONNECTED
@@ -46,7 +47,7 @@
 @RunWith(AndroidJUnit4::class)
 @SmallTest
 class LegacyTypeTrackerTest {
-    private val supportedTypes = arrayOf(TYPE_MOBILE, TYPE_WIFI, TYPE_ETHERNET)
+    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())
@@ -70,6 +71,26 @@
     }
 
     @Test
+    fun testSupl() {
+        val mobileNai = mock(NetworkAgentInfo::class.java)
+        mTracker.add(TYPE_MOBILE, mobileNai)
+        verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE)
+        reset(mMockService)
+        mTracker.add(TYPE_MOBILE_SUPL, mobileNai)
+        verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL)
+        reset(mMockService)
+        mTracker.remove(TYPE_MOBILE_SUPL, mobileNai, false /* wasDefault */)
+        verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL)
+        reset(mMockService)
+        mTracker.add(TYPE_MOBILE_SUPL, mobileNai)
+        verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL)
+        reset(mMockService)
+        mTracker.remove(mobileNai, false)
+        verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL)
+        verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE)
+    }
+
+    @Test
     fun testAddNetwork() {
         val mobileNai = mock(NetworkAgentInfo::class.java)
         val wifiNai = mock(NetworkAgentInfo::class.java)
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 1d29a82..4d42a61 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -192,8 +192,8 @@
 
         mService = new NetworkStatsService(
                 mServiceContext, mNetManager, mAlarmManager, wakeLock, mClock,
-                TelephonyManager.getDefault(), mSettings, mStatsFactory,
-                new NetworkStatsObservers(),  mStatsDir, getBaseDir(mStatsDir));
+                mServiceContext.getSystemService(TelephonyManager.class), mSettings,
+                mStatsFactory, new NetworkStatsObservers(),  mStatsDir, getBaseDir(mStatsDir));
         mHandlerThread = new HandlerThread("HandlerThread");
         mHandlerThread.start();
         Handler.Callback callback = new NetworkStatsService.HandlerCallback(mService);