Merge "Move module utils to the module package."
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 1e96872..540ea5c 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3152,9 +3152,9 @@
     }
 
     /**
-     * Set sign in error notification to visible or in visible
+     * Set sign in error notification to visible or invisible
      *
-     * {@hide}
+     * @hide
      * @deprecated Doesn't properly deal with multiple connected networks of the same type.
      */
     @Deprecated
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index fb01283..95a2f2e 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -31,6 +31,7 @@
 import android.net.ProxyInfo;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.INetworkActivityListener;
 import android.os.Messenger;
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
@@ -233,4 +234,10 @@
                 in PersistableBundle extras);
 
     void systemReady();
+
+    void registerNetworkActivityListener(in INetworkActivityListener l);
+
+    void unregisterNetworkActivityListener(in INetworkActivityListener l);
+
+    boolean isDefaultNetworkActive();
 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 55c1e08..2e1fbb7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -144,6 +144,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.INetworkActivityListener;
 import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
@@ -876,6 +877,10 @@
      */
     @VisibleForTesting
     public static class Dependencies {
+        public int getCallingUid() {
+            return Binder.getCallingUid();
+        }
+
         /**
          * Get system properties to use in ConnectivityService.
          */
@@ -1376,8 +1381,11 @@
             return;
         }
         final String action = blocked ? "BLOCKED" : "UNBLOCKED";
+        final NetworkRequest satisfiedRequest = nri.getSatisfiedRequest();
+        final int requestId =  satisfiedRequest != null
+                ? satisfiedRequest.requestId : nri.mRequests.get(0).requestId;
         mNetworkInfoBlockingLogs.log(String.format(
-                "%s %d(%d) on netId %d", action, nri.mUid, nri.request.requestId, net.getNetId()));
+                "%s %d(%d) on netId %d", action, nri.mUid, requestId, net.getNetId()));
     }
 
     /**
@@ -1408,7 +1416,7 @@
     @Override
     public NetworkInfo getActiveNetworkInfo() {
         enforceAccessPermission();
-        final int uid = Binder.getCallingUid();
+        final int uid = mDeps.getCallingUid();
         final NetworkState state = getUnfilteredActiveNetworkState(uid);
         filterNetworkStateForUid(state, uid, false);
         maybeLogBlockedNetworkInfo(state.networkInfo, uid);
@@ -1418,7 +1426,7 @@
     @Override
     public Network getActiveNetwork() {
         enforceAccessPermission();
-        return getActiveNetworkForUidInternal(Binder.getCallingUid(), false);
+        return getActiveNetworkForUidInternal(mDeps.getCallingUid(), false);
     }
 
     @Override
@@ -1458,7 +1466,7 @@
     // Public because it's used by mLockdownTracker.
     public NetworkInfo getActiveNetworkInfoUnfiltered() {
         enforceAccessPermission();
-        final int uid = Binder.getCallingUid();
+        final int uid = mDeps.getCallingUid();
         NetworkState state = getUnfilteredActiveNetworkState(uid);
         return state.networkInfo;
     }
@@ -1474,7 +1482,7 @@
     @Override
     public NetworkInfo getNetworkInfo(int networkType) {
         enforceAccessPermission();
-        final int uid = Binder.getCallingUid();
+        final int uid = mDeps.getCallingUid();
         if (getVpnUnderlyingNetworks(uid) != null) {
             // A VPN is active, so we may need to return one of its underlying networks. This
             // information is not available in LegacyTypeTracker, so we have to get it from
@@ -1519,7 +1527,7 @@
     @Override
     public Network getNetworkForType(int networkType) {
         enforceAccessPermission();
-        final int uid = Binder.getCallingUid();
+        final int uid = mDeps.getCallingUid();
         NetworkState state = getFilteredNetworkState(networkType, uid);
         if (!isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid, false)) {
             return state.network;
@@ -1566,7 +1574,7 @@
             result.put(
                     nai.network,
                     maybeSanitizeLocationInfoForCaller(
-                            nc, Binder.getCallingUid(), callingPackageName));
+                            nc, mDeps.getCallingUid(), callingPackageName));
         }
 
         synchronized (mVpns) {
@@ -1581,7 +1589,7 @@
                                 result.put(
                                         network,
                                         maybeSanitizeLocationInfoForCaller(
-                                                nc, Binder.getCallingUid(), callingPackageName));
+                                                nc, mDeps.getCallingUid(), callingPackageName));
                             }
                         }
                     }
@@ -1611,7 +1619,7 @@
     @Override
     public LinkProperties getActiveLinkProperties() {
         enforceAccessPermission();
-        final int uid = Binder.getCallingUid();
+        final int uid = mDeps.getCallingUid();
         NetworkState state = getUnfilteredActiveNetworkState(uid);
         if (state.linkProperties == null) return null;
         return linkPropertiesRestrictedForCallerPermissions(state.linkProperties,
@@ -1625,7 +1633,7 @@
         final LinkProperties lp = getLinkProperties(nai);
         if (lp == null) return null;
         return linkPropertiesRestrictedForCallerPermissions(
-                lp, Binder.getCallingPid(), Binder.getCallingUid());
+                lp, Binder.getCallingPid(), mDeps.getCallingUid());
     }
 
     // TODO - this should be ALL networks
@@ -1635,7 +1643,7 @@
         final LinkProperties lp = getLinkProperties(getNetworkAgentInfoForNetwork(network));
         if (lp == null) return null;
         return linkPropertiesRestrictedForCallerPermissions(
-                lp, Binder.getCallingPid(), Binder.getCallingUid());
+                lp, Binder.getCallingPid(), mDeps.getCallingUid());
     }
 
     @Nullable
@@ -1657,17 +1665,17 @@
         synchronized (nai) {
             if (nai.networkCapabilities == null) return null;
             return networkCapabilitiesRestrictedForCallerPermissions(
-                    nai.networkCapabilities, Binder.getCallingPid(), Binder.getCallingUid());
+                    nai.networkCapabilities, Binder.getCallingPid(), mDeps.getCallingUid());
         }
     }
 
     @Override
     public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName) {
-        mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackageName);
+        mAppOpsManager.checkPackage(mDeps.getCallingUid(), callingPackageName);
         enforceAccessPermission();
         return maybeSanitizeLocationInfoForCaller(
                 getNetworkCapabilitiesInternal(network),
-                Binder.getCallingUid(), callingPackageName);
+                mDeps.getCallingUid(), callingPackageName);
     }
 
     @VisibleForTesting
@@ -1705,16 +1713,17 @@
             return newNc;
         }
 
-        Binder.withCleanCallingIdentity(
-                () -> {
-                    if (!mLocationPermissionChecker.checkLocationPermission(
-                            callerPkgName, null /* featureId */, callerUid, null /* message */)) {
-                        // Caller does not have the requisite location permissions. Reset the
-                        // owner's UID in the NetworkCapabilities.
-                        newNc.setOwnerUid(INVALID_UID);
-                    }
-                }
-        );
+        final long token = Binder.clearCallingIdentity();
+        try {
+            if (!mLocationPermissionChecker.checkLocationPermission(
+                    callerPkgName, null /* featureId */, callerUid, null /* message */)) {
+                // Caller does not have the requisite location permissions. Reset the
+                // owner's UID in the NetworkCapabilities.
+                newNc.setOwnerUid(INVALID_UID);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
 
         return newNc;
     }
@@ -1755,7 +1764,7 @@
     }
 
     private void restrictBackgroundRequestForCaller(NetworkCapabilities nc) {
-        if (!mPermissionMonitor.hasUseBackgroundNetworksPermission(Binder.getCallingUid())) {
+        if (!mPermissionMonitor.hasUseBackgroundNetworksPermission(mDeps.getCallingUid())) {
             nc.addCapability(NET_CAPABILITY_FOREGROUND);
         }
     }
@@ -1808,7 +1817,7 @@
         // requestRouteToHost. In Q, GnssLocationProvider is changed to not call requestRouteToHost
         // for devices launched with Q and above. However, existing devices upgrading to Q and
         // above must continued to be supported for few more releases.
-        if (isSystem(Binder.getCallingUid()) && SystemProperties.getInt(
+        if (isSystem(mDeps.getCallingUid()) && SystemProperties.getInt(
                 "ro.product.first_api_level", 0) > Build.VERSION_CODES.P) {
             log("This method exists only for app backwards compatibility"
                     + " and must not be called by system services.");
@@ -1874,7 +1883,7 @@
             return false;
         }
 
-        final int uid = Binder.getCallingUid();
+        final int uid = mDeps.getCallingUid();
         final long token = Binder.clearCallingIdentity();
         try {
             LinkProperties lp;
@@ -2294,7 +2303,7 @@
      */
     @Override
     public void systemReady() {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+        if (mDeps.getCallingUid() != Process.SYSTEM_UID) {
             throw new SecurityException("Calling Uid is not system uid.");
         }
         systemReadyInternal();
@@ -2332,6 +2341,31 @@
     }
 
     /**
+     * Start listening for default data network activity state changes.
+     */
+    @Override
+    public void registerNetworkActivityListener(@NonNull INetworkActivityListener l) {
+        // TODO: Replace network activity listener registry in ConnectivityManager from NMS to here
+    }
+
+    /**
+     * Stop listening for default data network activity state changes.
+     */
+    @Override
+    public void unregisterNetworkActivityListener(@NonNull INetworkActivityListener l) {
+        // TODO: Replace network activity listener registry in ConnectivityManager from NMS to here
+    }
+
+    /**
+     * Check whether the default network radio is currently active.
+     */
+    @Override
+    public boolean isDefaultNetworkActive() {
+        // TODO: Replace isNetworkActive() in NMS.
+        return false;
+    }
+
+    /**
      * Setup data activity tracking for the given network.
      *
      * Every {@code setupDataActivityTracking} should be paired with a
@@ -2520,7 +2554,7 @@
         if (context.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
             pw.println("Permission Denial: can't dump " + tag + " from from pid="
-                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+                    + Binder.getCallingPid() + ", uid=" + mDeps.getCallingUid()
                     + " due to missing android.permission.DUMP permission");
             return false;
         } else {
@@ -2701,7 +2735,7 @@
      * Return an array of all current NetworkRequest sorted by request id.
      */
     @VisibleForTesting
-    protected NetworkRequestInfo[] requestsSortedById() {
+    NetworkRequestInfo[] requestsSortedById() {
         NetworkRequestInfo[] requests = new NetworkRequestInfo[0];
         requests = mNetworkRequests.values().toArray(requests);
         // Sort the array based off the NRI containing the min requestId in its requests.
@@ -3551,30 +3585,58 @@
             return false;
         }
         for (NetworkRequestInfo nri : mNetworkRequests.values()) {
-            if (reason == UnneededFor.LINGER && nri.request.isBackgroundRequest()) {
+            if (reason == UnneededFor.LINGER
+                    && !nri.isMultilayerRequest()
+                    && nri.mRequests.get(0).isBackgroundRequest()) {
                 // Background requests don't affect lingering.
                 continue;
             }
 
-            // 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.
-            if (nri.request.isRequest() && nai.satisfies(nri.request) &&
-                    (nai.isSatisfyingRequest(nri.request.requestId) ||
-                    // Note that this catches two important cases:
-                    // 1. Unvalidated cellular will not be reaped when unvalidated WiFi
-                    //    is currently satisfying the request.  This is desirable when
-                    //    cellular ends up validating but WiFi does not.
-                    // 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.
-                    nri.mSatisfier.getCurrentScore()
-                            < nai.getCurrentScoreAsValidated())) {
+            if (isNetworkPotentialSatisfier(nai, nri)) {
                 return false;
             }
         }
         return true;
     }
 
+    private boolean isNetworkPotentialSatisfier(
+            @NonNull final NetworkAgentInfo candidate, @NonNull final NetworkRequestInfo nri) {
+        // listen requests won't keep up a network satisfying it. If this is not a multilayer
+        // request, we can return immediately. For multilayer requests, we have to check to see if
+        // any of the multilayer requests may have a potential satisfier.
+        if (!nri.isMultilayerRequest() && nri.mRequests.get(0).isListen()) {
+            return false;
+        }
+        for (final NetworkRequest req : nri.mRequests) {
+            // As non-multilayer listen requests have already returned, the below would only happen
+            // for a multilayer request therefore continue to the next request if available.
+            if (req.isListen()) {
+                continue;
+            }
+            // 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.
+            if (candidate.satisfies(req)) {
+                // As soon as a network is found that satisfies a request, return. Specifically for
+                // multilayer requests, returning as soon as a NetworkAgentInfo satisfies a request
+                // is important so as to not evaluate lower priority requests further in
+                // nri.mRequests.
+                final boolean isNetworkNeeded = candidate.isSatisfyingRequest(req.requestId)
+                        // Note that this catches two important cases:
+                        // 1. Unvalidated cellular will not be reaped when unvalidated WiFi
+                        //    is currently satisfying the request.  This is desirable when
+                        //    cellular ends up validating but WiFi does not.
+                        // 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.
+                        || nri.mSatisfier.getCurrentScore()
+                        < candidate.getCurrentScoreAsValidated();
+                return isNetworkNeeded;
+            }
+        }
+
+        return false;
+    }
+
     private NetworkRequestInfo getNriForAppRequest(
             NetworkRequest request, int callingUid, String requestedOperation) {
         final NetworkRequestInfo nri = mNetworkRequests.get(request);
@@ -3871,8 +3933,12 @@
                 new CaptivePortal(new CaptivePortalImpl(network).asBinder()));
         appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
 
-        Binder.withCleanCallingIdentity(() ->
-                mContext.startActivityAsUser(appIntent, UserHandle.CURRENT));
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mContext.startActivityAsUser(appIntent, UserHandle.CURRENT);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     private class CaptivePortalImpl extends ICaptivePortal.Stub {
@@ -3900,7 +3966,7 @@
 
             if (request == CaptivePortal.APP_REQUEST_REEVALUATION_REQUIRED) {
                 checkNetworkStackPermission();
-                nm.forceReevaluation(Binder.getCallingUid());
+                nm.forceReevaluation(mDeps.getCallingUid());
             }
         }
 
@@ -4367,7 +4433,7 @@
     public void reportNetworkConnectivity(Network network, boolean hasConnectivity) {
         enforceAccessPermission();
         enforceInternetPermission();
-        final int uid = Binder.getCallingUid();
+        final int uid = mDeps.getCallingUid();
         final int connectivityInfo = encodeBool(hasConnectivity);
 
         // Handle ConnectivityDiagnostics event before attempting to revalidate the network. This
@@ -4437,13 +4503,13 @@
         if (globalProxy != null) return globalProxy;
         if (network == null) {
             // Get the network associated with the calling UID.
-            final Network activeNetwork = getActiveNetworkForUidInternal(Binder.getCallingUid(),
+            final Network activeNetwork = getActiveNetworkForUidInternal(mDeps.getCallingUid(),
                     true);
             if (activeNetwork == null) {
                 return null;
             }
             return getLinkPropertiesProxyInfo(activeNetwork);
-        } else if (mDeps.queryUserAccess(Binder.getCallingUid(), network.getNetId())) {
+        } else if (mDeps.queryUserAccess(mDeps.getCallingUid(), network.getNetId())) {
             // Don't call getLinkProperties() as it requires ACCESS_NETWORK_STATE permission, which
             // caller may not have.
             return getLinkPropertiesProxyInfo(network);
@@ -4612,7 +4678,7 @@
      */
     @Override
     public ParcelFileDescriptor establishVpn(VpnConfig config) {
-        int user = UserHandle.getUserId(Binder.getCallingUid());
+        int user = UserHandle.getUserId(mDeps.getCallingUid());
         synchronized (mVpns) {
             throwIfLockdownEnabled();
             return mVpns.get(user).establish(config);
@@ -4633,7 +4699,7 @@
      */
     @Override
     public boolean provisionVpnProfile(@NonNull VpnProfile profile, @NonNull String packageName) {
-        final int user = UserHandle.getUserId(Binder.getCallingUid());
+        final int user = UserHandle.getUserId(mDeps.getCallingUid());
         synchronized (mVpns) {
             return mVpns.get(user).provisionVpnProfile(packageName, profile, mKeyStore);
         }
@@ -4651,7 +4717,7 @@
      */
     @Override
     public void deleteVpnProfile(@NonNull String packageName) {
-        final int user = UserHandle.getUserId(Binder.getCallingUid());
+        final int user = UserHandle.getUserId(mDeps.getCallingUid());
         synchronized (mVpns) {
             mVpns.get(user).deleteVpnProfile(packageName, mKeyStore);
         }
@@ -4668,7 +4734,7 @@
      */
     @Override
     public void startVpnProfile(@NonNull String packageName) {
-        final int user = UserHandle.getUserId(Binder.getCallingUid());
+        final int user = UserHandle.getUserId(mDeps.getCallingUid());
         synchronized (mVpns) {
             throwIfLockdownEnabled();
             mVpns.get(user).startVpnProfile(packageName, mKeyStore);
@@ -4685,7 +4751,7 @@
      */
     @Override
     public void stopVpnProfile(@NonNull String packageName) {
-        final int user = UserHandle.getUserId(Binder.getCallingUid());
+        final int user = UserHandle.getUserId(mDeps.getCallingUid());
         synchronized (mVpns) {
             mVpns.get(user).stopVpnProfile(packageName);
         }
@@ -4697,7 +4763,7 @@
      */
     @Override
     public void startLegacyVpn(VpnProfile profile) {
-        int user = UserHandle.getUserId(Binder.getCallingUid());
+        int user = UserHandle.getUserId(mDeps.getCallingUid());
         final LinkProperties egress = getActiveLinkProperties();
         if (egress == null) {
             throw new IllegalStateException("Missing active network connection");
@@ -4846,7 +4912,7 @@
 
     @Override
     public boolean updateLockdownVpn() {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+        if (mDeps.getCallingUid() != Process.SYSTEM_UID) {
             logw("Lockdown VPN only available to AID_SYSTEM");
             return false;
         }
@@ -4868,7 +4934,7 @@
                     setLockdownTracker(null);
                     return true;
                 }
-                int user = UserHandle.getUserId(Binder.getCallingUid());
+                int user = UserHandle.getUserId(mDeps.getCallingUid());
                 Vpn vpn = mVpns.get(user);
                 if (vpn == null) {
                     logw("VPN for user " + user + " not ready yet. Skipping lockdown");
@@ -5433,7 +5499,7 @@
             messenger = null;
             mBinder = null;
             mPid = getCallingPid();
-            mUid = getCallingUid();
+            mUid = mDeps.getCallingUid();
             enforceRequestCountLimit();
         }
 
@@ -5445,7 +5511,7 @@
             ensureAllNetworkRequestsHaveType(mRequests);
             mBinder = binder;
             mPid = getCallingPid();
-            mUid = getCallingUid();
+            mUid = mDeps.getCallingUid();
             mPendingIntent = null;
             enforceRequestCountLimit();
 
@@ -5460,6 +5526,10 @@
             this(r, null);
         }
 
+        boolean isMultilayerRequest() {
+            return mRequests.size() > 1;
+        }
+
         private List<NetworkRequest> initializeRequests(NetworkRequest r) {
             final ArrayList<NetworkRequest> tempRequests = new ArrayList<>();
             tempRequests.add(new NetworkRequest(r));
@@ -5501,7 +5571,7 @@
         public void binderDied() {
             log("ConnectivityService NetworkRequestInfo binderDied(" +
                     mRequests + ", " + mBinder + ")");
-            releaseNetworkRequest(mRequests);
+            releaseNetworkRequests(mRequests);
         }
 
         @Override
@@ -5534,13 +5604,15 @@
         mAppOpsManager.checkPackage(callerUid, callerPackageName);
     }
 
-    private ArrayList<Integer> getSignalStrengthThresholds(NetworkAgentInfo nai) {
+    private ArrayList<Integer> getSignalStrengthThresholds(@NonNull final NetworkAgentInfo nai) {
         final SortedSet<Integer> thresholds = new TreeSet<>();
         synchronized (nai) {
-            for (NetworkRequestInfo nri : mNetworkRequests.values()) {
-                if (nri.request.networkCapabilities.hasSignalStrength() &&
-                        nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
-                    thresholds.add(nri.request.networkCapabilities.getSignalStrength());
+            for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
+                for (final NetworkRequest req : nri.mRequests) {
+                    if (req.networkCapabilities.hasSignalStrength()
+                            && nai.satisfiesImmutableCapabilitiesOf(req)) {
+                        thresholds.add(req.networkCapabilities.getSignalStrength());
+                    }
                 }
             }
         }
@@ -5588,7 +5660,7 @@
     }
 
     private boolean checkUnsupportedStartingFrom(int version, String callingPackageName) {
-        final UserHandle user = UserHandle.getUserHandleForUid(Binder.getCallingUid());
+        final UserHandle user = UserHandle.getUserHandleForUid(mDeps.getCallingUid());
         final PackageManager pm =
                 mContext.createContextAsUser(user, 0 /* flags */).getPackageManager();
         try {
@@ -5608,7 +5680,7 @@
                 throw new SecurityException("Insufficient permissions to specify legacy type");
             }
         }
-        final int callingUid = Binder.getCallingUid();
+        final int callingUid = mDeps.getCallingUid();
         final NetworkRequest.Type type = (networkCapabilities == null)
                 ? NetworkRequest.Type.TRACK_DEFAULT
                 : NetworkRequest.Type.REQUEST;
@@ -5678,7 +5750,7 @@
         if (nai != null) {
             nai.asyncChannel.sendMessage(android.net.NetworkAgent.CMD_REQUEST_BANDWIDTH_UPDATE);
             synchronized (mBandwidthRequests) {
-                final int uid = Binder.getCallingUid();
+                final int uid = mDeps.getCallingUid();
                 Integer uidReqs = mBandwidthRequests.get(uid);
                 if (uidReqs == null) {
                     uidReqs = 0;
@@ -5695,7 +5767,7 @@
     }
 
     private void enforceMeteredApnPolicy(NetworkCapabilities networkCapabilities) {
-        final int uid = Binder.getCallingUid();
+        final int uid = mDeps.getCallingUid();
         if (isSystem(uid)) {
             // Exemption for system uid.
             return;
@@ -5715,7 +5787,7 @@
             PendingIntent operation, @NonNull String callingPackageName,
             @Nullable String callingAttributionTag) {
         Objects.requireNonNull(operation, "PendingIntent cannot be null.");
-        final int callingUid = Binder.getCallingUid();
+        final int callingUid = mDeps.getCallingUid();
         networkCapabilities = new NetworkCapabilities(networkCapabilities);
         enforceNetworkRequestPermissions(networkCapabilities, callingPackageName,
                 callingAttributionTag);
@@ -5774,7 +5846,7 @@
     @Override
     public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
             Messenger messenger, IBinder binder, @NonNull String callingPackageName) {
-        final int callingUid = Binder.getCallingUid();
+        final int callingUid = mDeps.getCallingUid();
         if (!hasWifiNetworkListenPermission(networkCapabilities)) {
             enforceAccessPermission();
         }
@@ -5804,7 +5876,7 @@
     public void pendingListenForNetwork(NetworkCapabilities networkCapabilities,
             PendingIntent operation, @NonNull String callingPackageName) {
         Objects.requireNonNull(operation, "PendingIntent cannot be null.");
-        final int callingUid = Binder.getCallingUid();
+        final int callingUid = mDeps.getCallingUid();
         if (!hasWifiNetworkListenPermission(networkCapabilities)) {
             enforceAccessPermission();
         }
@@ -5827,7 +5899,7 @@
         return mNextNetworkProviderId.getAndIncrement();
     }
 
-    private void releaseNetworkRequest(List<NetworkRequest> networkRequests) {
+    private void releaseNetworkRequests(List<NetworkRequest> networkRequests) {
         for (int i = 0; i < networkRequests.size(); i++) {
             releaseNetworkRequest(networkRequests.get(i));
         }
@@ -5905,7 +5977,7 @@
         } else {
             enforceNetworkFactoryPermission();
         }
-        mHandler.post(() -> handleReleaseNetworkRequest(request, Binder.getCallingUid(), true));
+        mHandler.post(() -> handleReleaseNetworkRequest(request, mDeps.getCallingUid(), true));
     }
 
     // NOTE: Accessed on multiple threads, must be synchronized on itself.
@@ -5999,7 +6071,7 @@
             enforceNetworkFactoryPermission();
         }
 
-        final int uid = Binder.getCallingUid();
+        final int uid = mDeps.getCallingUid();
         final long token = Binder.clearCallingIdentity();
         try {
             return registerNetworkAgentInternal(messenger, networkInfo, linkProperties,
@@ -7653,7 +7725,7 @@
 
     @Override
     public boolean addVpnAddress(String address, int prefixLength) {
-        int user = UserHandle.getUserId(Binder.getCallingUid());
+        int user = UserHandle.getUserId(mDeps.getCallingUid());
         synchronized (mVpns) {
             throwIfLockdownEnabled();
             return mVpns.get(user).addAddress(address, prefixLength);
@@ -7662,7 +7734,7 @@
 
     @Override
     public boolean removeVpnAddress(String address, int prefixLength) {
-        int user = UserHandle.getUserId(Binder.getCallingUid());
+        int user = UserHandle.getUserId(mDeps.getCallingUid());
         synchronized (mVpns) {
             throwIfLockdownEnabled();
             return mVpns.get(user).removeAddress(address, prefixLength);
@@ -7671,7 +7743,7 @@
 
     @Override
     public boolean setUnderlyingNetworksForVpn(Network[] networks) {
-        int user = UserHandle.getUserId(Binder.getCallingUid());
+        int user = UserHandle.getUserId(mDeps.getCallingUid());
         final boolean success;
         synchronized (mVpns) {
             throwIfLockdownEnabled();
@@ -7759,10 +7831,13 @@
 
         final int userId = UserHandle.getCallingUserId();
 
-        Binder.withCleanCallingIdentity(() -> {
+        final long token = Binder.clearCallingIdentity();
+        try {
             final IpMemoryStore ipMemoryStore = IpMemoryStore.getMemoryStore(mContext);
             ipMemoryStore.factoryReset();
-        });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
 
         // Turn airplane mode off
         setAirplaneMode(false);
@@ -7898,7 +7973,7 @@
 
     @GuardedBy("mVpns")
     private Vpn getVpnIfOwner() {
-        return getVpnIfOwner(Binder.getCallingUid());
+        return getVpnIfOwner(mDeps.getCallingUid());
     }
 
     @GuardedBy("mVpns")
@@ -8376,7 +8451,7 @@
             throw new IllegalArgumentException("ConnectivityManager.TYPE_* are deprecated."
                     + " Please use NetworkCapabilities instead.");
         }
-        final int callingUid = Binder.getCallingUid();
+        final int callingUid = mDeps.getCallingUid();
         mAppOpsManager.checkPackage(callingUid, callingPackageName);
 
         // This NetworkCapabilities is only used for matching to Networks. Clear out its owner uid
@@ -8411,7 +8486,7 @@
                 mConnectivityDiagnosticsHandler.obtainMessage(
                         ConnectivityDiagnosticsHandler
                                 .EVENT_UNREGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK,
-                        Binder.getCallingUid(),
+                        mDeps.getCallingUid(),
                         0,
                         callback));
     }
@@ -8427,7 +8502,7 @@
         }
 
         final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
-        if (nai == null || nai.creatorUid != Binder.getCallingUid()) {
+        if (nai == null || nai.creatorUid != mDeps.getCallingUid()) {
             throw new SecurityException("Data Stall simulation is only possible for network "
                 + "creators");
         }
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index 655d8ab..e8687e5 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -107,23 +107,23 @@
 
         String ifacePrefix = isTun ? TEST_TUN_PREFIX : TEST_TAP_PREFIX;
         String iface = ifacePrefix + sTestTunIndex.getAndIncrement();
-        return Binder.withCleanCallingIdentity(
-                () -> {
-                    try {
-                        ParcelFileDescriptor tunIntf =
-                                ParcelFileDescriptor.adoptFd(jniCreateTunTap(isTun, iface));
-                        for (LinkAddress addr : linkAddrs) {
-                            mNetd.interfaceAddAddress(
-                                    iface,
-                                    addr.getAddress().getHostAddress(),
-                                    addr.getPrefixLength());
-                        }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            ParcelFileDescriptor tunIntf =
+                    ParcelFileDescriptor.adoptFd(jniCreateTunTap(isTun, iface));
+            for (LinkAddress addr : linkAddrs) {
+                mNetd.interfaceAddAddress(
+                        iface,
+                        addr.getAddress().getHostAddress(),
+                        addr.getPrefixLength());
+            }
 
-                        return new TestNetworkInterface(tunIntf, iface);
-                    } catch (RemoteException e) {
-                        throw e.rethrowFromSystemServer();
-                    }
-                });
+            return new TestNetworkInterface(tunIntf, iface);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     /**
@@ -317,7 +317,12 @@
 
         try {
             // This requires NETWORK_STACK privileges.
-            Binder.withCleanCallingIdentity(() -> mNMS.setInterfaceUp(iface));
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mNMS.setInterfaceUp(iface);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
 
             // Synchronize all accesses to mTestNetworkTracker to prevent the case where:
             // 1. TestNetworkAgent successfully binds to death of binder
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 7795ed3..3d71b0a 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -334,7 +334,13 @@
      */
     public void setProvNotificationVisible(boolean visible, int id, String action) {
         if (visible) {
-            Intent intent = new Intent(action);
+            // For legacy purposes, action is sent as the action + the phone ID from DcTracker.
+            // Split the string here and send the phone ID as an extra instead.
+            String[] splitAction = action.split(":");
+            Intent intent = new Intent(splitAction[0]);
+            try {
+                intent.putExtra("provision.phone.id", Integer.parseInt(splitAction[1]));
+            } catch (NumberFormatException ignored) { }
             PendingIntent pendingIntent = PendingIntent.getBroadcast(
                     mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE);
             showNotification(id, NotificationType.SIGN_IN, null, null, pendingIntent, false);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 8c403f1..a68044a 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -39,6 +39,7 @@
 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_VPN;
 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;
@@ -101,6 +102,8 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.AdditionalMatchers.aryEq;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.argThat;
@@ -132,6 +135,7 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -140,6 +144,8 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.location.LocationManager;
@@ -176,6 +182,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkFactory;
 import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
@@ -332,12 +339,13 @@
     private static final String WIFI_WOL_IFNAME = "test_wlan_wol";
     private static final String VPN_IFNAME = "tun10042";
     private static final String TEST_PACKAGE_NAME = "com.android.test.package";
-    private static final String[] EMPTY_STRING_ARRAY = new String[0];
+    private static final String ALWAYS_ON_PACKAGE = "com.android.test.alwaysonvpn";
 
     private static final String INTERFACE_NAME = "interface";
 
     private MockContext mServiceContext;
     private HandlerThread mCsHandlerThread;
+    private ConnectivityService.Dependencies mDeps;
     private ConnectivityService mService;
     private WrappedConnectivityManager mCm;
     private TestNetworkAgentWrapper mWiFiNetworkAgent;
@@ -353,6 +361,7 @@
     @Mock IIpConnectivityMetrics mIpConnectivityMetrics;
     @Mock IpConnectivityMetrics.Logger mMetricsService;
     @Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
+    @Mock DeviceIdleInternal mDeviceIdleInternal;
     @Mock INetworkManagementService mNetworkManagementService;
     @Mock INetworkStatsService mStatsService;
     @Mock IBatteryStats mBatteryStatsService;
@@ -450,6 +459,15 @@
         }
 
         @Override
+        public ComponentName startService(Intent service) {
+            final String action = service.getAction();
+            if (!VpnConfig.SERVICE_INTERFACE.equals(action)) {
+                fail("Attempt to start unknown service, action=" + action);
+            }
+            return new ComponentName(service.getPackage(), "com.android.test.Service");
+        }
+
+        @Override
         public Object getSystemService(String name) {
             if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm;
             if (Context.NOTIFICATION_SERVICE.equals(name)) return mNotificationManager;
@@ -1055,9 +1073,19 @@
         private VpnInfo mVpnInfo;
 
         public MockVpn(int userId) {
-            super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
-                    mMockNetd, userId, mock(KeyStore.class));
-            mConfig = new VpnConfig();
+            super(startHandlerThreadAndReturnLooper(), mServiceContext,
+                    new Dependencies() {
+                        @Override
+                        public boolean isCallerSystem() {
+                            return true;
+                        }
+
+                        @Override
+                        public DeviceIdleInternal getDeviceIdleInternal() {
+                            return mDeviceIdleInternal;
+                        }
+                    },
+                    mNetworkManagementService, mMockNetd, userId, mock(KeyStore.class));
         }
 
         public void setUids(Set<UidRange> uids) {
@@ -1086,9 +1114,16 @@
             return mVpnType;
         }
 
+        private LinkProperties makeLinkProperties() {
+            final LinkProperties lp = new LinkProperties();
+            lp.setInterfaceName(VPN_IFNAME);
+            return lp;
+        }
+
         private void registerAgent(boolean isAlwaysMetered, Set<UidRange> uids, LinkProperties lp)
                 throws Exception {
             if (mAgentRegistered) throw new IllegalStateException("already registered");
+            mConfig = new VpnConfig();
             setUids(uids);
             if (!isAlwaysMetered) mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
             mInterface = VPN_IFNAME;
@@ -1101,12 +1136,13 @@
             verify(mMockNetd, never())
                     .networkRemoveUidRanges(eq(mMockVpn.getNetId()), any());
             mAgentRegistered = true;
+            updateState(NetworkInfo.DetailedState.CONNECTED, "registerAgent");
             mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
             mNetworkAgent = mMockNetworkAgent.getNetworkAgent();
         }
 
         private void registerAgent(Set<UidRange> uids) throws Exception {
-            registerAgent(false /* isAlwaysMetered */, uids, new LinkProperties());
+            registerAgent(false /* isAlwaysMetered */, uids, makeLinkProperties());
         }
 
         private void connect(boolean validated, boolean hasInternet, boolean isStrictMode) {
@@ -1142,12 +1178,12 @@
         public void establishForMyUid(boolean validated, boolean hasInternet, boolean isStrictMode)
                 throws Exception {
             final int uid = Process.myUid();
-            establish(new LinkProperties(), uid, uidRangesForUid(uid), validated, hasInternet,
+            establish(makeLinkProperties(), uid, uidRangesForUid(uid), validated, hasInternet,
                     isStrictMode);
         }
 
         public void establishForMyUid() throws Exception {
-            establishForMyUid(new LinkProperties());
+            establishForMyUid(makeLinkProperties());
         }
 
         public void sendLinkProperties(LinkProperties lp) {
@@ -1155,7 +1191,10 @@
         }
 
         public void disconnect() {
-            if (mMockNetworkAgent != null) mMockNetworkAgent.disconnect();
+            if (mMockNetworkAgent != null) {
+                mMockNetworkAgent.disconnect();
+                updateState(NetworkInfo.DetailedState.DISCONNECTED, "disconnect");
+            }
             mAgentRegistered = false;
         }
 
@@ -1229,6 +1268,17 @@
         fail("ConditionVariable was blocked for more than " + TIMEOUT_MS + "ms");
     }
 
+    private void registerNetworkCallbackAsUid(NetworkRequest request, NetworkCallback callback,
+            int uid) {
+        when(mDeps.getCallingUid()).thenReturn(uid);
+        try {
+            mCm.registerNetworkCallback(request, callback);
+            waitForIdle();
+        } finally {
+            returnRealCallingUid();
+        }
+    }
+
     private static final int VPN_USER = 0;
     private static final int APP1_UID = UserHandle.getUid(VPN_USER, 10100);
     private static final int APP2_UID = UserHandle.getUid(VPN_USER, 10101);
@@ -1271,7 +1321,8 @@
         initAlarmManager(mAlarmManager, mAlarmManagerThread.getThreadHandler());
 
         mCsHandlerThread = new HandlerThread("TestConnectivityService");
-        final ConnectivityService.Dependencies deps = makeDependencies();
+        mDeps = makeDependencies();
+        returnRealCallingUid();
         mService = new ConnectivityService(mServiceContext,
                 mNetworkManagementService,
                 mStatsService,
@@ -1279,9 +1330,9 @@
                 mMockDnsResolver,
                 mock(IpConnectivityLog.class),
                 mMockNetd,
-                deps);
+                mDeps);
         mService.mLingerDelayMs = TEST_LINGER_DELAY_MS;
-        verify(deps).makeMultinetworkPolicyTracker(any(), any(), any());
+        verify(mDeps).makeMultinetworkPolicyTracker(any(), any(), any());
 
         final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
                 ArgumentCaptor.forClass(INetworkPolicyListener.class);
@@ -1301,6 +1352,10 @@
         setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
     }
 
+    private void returnRealCallingUid() {
+        doAnswer((invocationOnMock) -> Binder.getCallingUid()).when(mDeps).getCallingUid();
+    }
+
     private ConnectivityService.Dependencies makeDependencies() {
         doReturn(TEST_TCP_INIT_RWND).when(mSystemProperties)
                 .getInt("net.tcp.default_init_rwnd", 0);
@@ -1376,13 +1431,13 @@
     }
 
     private void mockDefaultPackages() throws Exception {
-        final String testPackageName = mContext.getPackageName();
-        final PackageInfo testPackageInfo = mContext.getPackageManager().getPackageInfo(
-                testPackageName, PackageManager.GET_PERMISSIONS);
+        final String myPackageName = mContext.getPackageName();
+        final PackageInfo myPackageInfo = mContext.getPackageManager().getPackageInfo(
+                myPackageName, PackageManager.GET_PERMISSIONS);
         when(mPackageManager.getPackagesForUid(Binder.getCallingUid())).thenReturn(
-                new String[] {testPackageName});
-        when(mPackageManager.getPackageInfoAsUser(eq(testPackageName), anyInt(),
-                eq(UserHandle.getCallingUserId()))).thenReturn(testPackageInfo);
+                new String[] {myPackageName});
+        when(mPackageManager.getPackageInfoAsUser(eq(myPackageName), anyInt(),
+                eq(UserHandle.getCallingUserId()))).thenReturn(myPackageInfo);
 
         when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
                 Arrays.asList(new PackageInfo[] {
@@ -1390,6 +1445,25 @@
                         buildPackageInfo(/* SYSTEM */ false, APP2_UID),
                         buildPackageInfo(/* SYSTEM */ false, VPN_UID)
                 }));
+
+        // Create a fake always-on VPN package.
+        final int userId = UserHandle.getCallingUserId();
+        final ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.targetSdkVersion = Build.VERSION_CODES.R;  // Always-on supported in N+.
+        when(mPackageManager.getApplicationInfoAsUser(eq(ALWAYS_ON_PACKAGE), anyInt(),
+                eq(userId))).thenReturn(applicationInfo);
+
+        // Minimal mocking to keep Vpn#isAlwaysOnPackageSupported happy.
+        ResolveInfo rInfo = new ResolveInfo();
+        rInfo.serviceInfo = new ServiceInfo();
+        rInfo.serviceInfo.metaData = new Bundle();
+        final List<ResolveInfo> services = Arrays.asList(new ResolveInfo[]{rInfo});
+        when(mPackageManager.queryIntentServicesAsUser(any(), eq(PackageManager.GET_META_DATA),
+                eq(userId))).thenReturn(services);
+        when(mPackageManager.getPackageUidAsUser(TEST_PACKAGE_NAME, userId))
+                .thenReturn(Process.myUid());
+        when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, userId))
+                .thenReturn(VPN_UID);
     }
 
     private void verifyActiveNetwork(int transport) {
@@ -2252,10 +2326,10 @@
     }
 
     private void grantUsingBackgroundNetworksPermissionForUid(final int uid) throws Exception {
-        final String testPackageName = mContext.getPackageName();
-        when(mPackageManager.getPackageInfo(eq(testPackageName), eq(GET_PERMISSIONS)))
+        final String myPackageName = mContext.getPackageName();
+        when(mPackageManager.getPackageInfo(eq(myPackageName), eq(GET_PERMISSIONS)))
                 .thenReturn(buildPackageInfo(true, uid));
-        mService.mPermissionMonitor.onPackageAdded(testPackageName, uid);
+        mService.mPermissionMonitor.onPackageAdded(myPackageName, uid);
     }
 
     @Test
@@ -5831,10 +5905,21 @@
         assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
     }
 
+    private void assertDefaultNetworkCapabilities(int userId, NetworkAgentWrapper... networks) {
+        final NetworkCapabilities[] defaultCaps = mService.getDefaultNetworkCapabilitiesForUser(
+                userId, "com.android.calling.package");
+        final String defaultCapsString = Arrays.toString(defaultCaps);
+        assertEquals(defaultCapsString, defaultCaps.length, networks.length);
+        final Set<NetworkCapabilities> defaultCapsSet = new ArraySet<>(defaultCaps);
+        for (NetworkAgentWrapper network : networks) {
+            final NetworkCapabilities nc = mCm.getNetworkCapabilities(network.getNetwork());
+            final String msg = "Did not find " + nc + " in " + Arrays.toString(defaultCaps);
+            assertTrue(msg, defaultCapsSet.contains(nc));
+        }
+    }
+
     @Test
     public void testVpnSetUnderlyingNetworks() throws Exception {
-        final int uid = Process.myUid();
-
         final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
         final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder()
                 .removeCapability(NET_CAPABILITY_NOT_VPN)
@@ -5857,6 +5942,9 @@
         // A VPN without underlying networks is not suspended.
         assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
 
+        final int userId = UserHandle.getUserId(Process.myUid());
+        assertDefaultNetworkCapabilities(userId /* no networks */);
+
         // Connect cell and use it as an underlying network.
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
@@ -5870,6 +5958,7 @@
                 && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
                 && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+        assertDefaultNetworkCapabilities(userId, mCellNetworkAgent);
 
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
@@ -5884,6 +5973,7 @@
                 && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
                 && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+        assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
 
         // Don't disconnect, but note the VPN is not using wifi any more.
         mService.setUnderlyingNetworksForVpn(
@@ -5894,6 +5984,9 @@
                 && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
                 && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+        // The return value of getDefaultNetworkCapabilitiesForUser always includes the default
+        // network (wifi) as well as the underlying networks (cell).
+        assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
 
         // Remove NOT_SUSPENDED from the only network and observe VPN is now suspended.
         mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
@@ -5922,6 +6015,7 @@
                 && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
                 && caps.hasCapability(NET_CAPABILITY_NOT_METERED)
                 && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+        assertDefaultNetworkCapabilities(userId, mWiFiNetworkAgent);
 
         // Use both again.
         mService.setUnderlyingNetworksForVpn(
@@ -5932,6 +6026,7 @@
                 && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
                 && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+        assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
 
         // Cell is suspended again. As WiFi is not, this should not cause a callback.
         mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
@@ -5949,6 +6044,7 @@
         // a bug in ConnectivityService, but as the SUSPENDED and RESUMED callbacks have never
         // been public and are deprecated and slated for removal, there is no sense in spending
         // resources fixing this bug now.
+        assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
 
         // Use both again.
         mService.setUnderlyingNetworksForVpn(
@@ -5961,6 +6057,7 @@
                 && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
         // As above, the RESUMED callback not being sent here is a bug, but not a bug that's
         // worth anybody's time to fix.
+        assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
 
         // Disconnect cell. Receive update without even removing the dead network from the
         // underlying networks – it's dead anyway. Not metered any more.
@@ -5969,6 +6066,7 @@
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
                 && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
                 && caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+        assertDefaultNetworkCapabilities(userId, mWiFiNetworkAgent);
 
         // Disconnect wifi too. No underlying networks means this is now metered.
         mWiFiNetworkAgent.disconnect();
@@ -5976,6 +6074,11 @@
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
                 && !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+        // When a network disconnects, the callbacks are fired before all state is updated, so for a
+        // short time, synchronous calls will behave as if the network is still connected. Wait for
+        // things to settle.
+        waitForIdle();
+        assertDefaultNetworkCapabilities(userId /* no networks */);
 
         mMockVpn.disconnect();
     }
@@ -6276,6 +6379,7 @@
         // Despite VPN using WiFi (which is unmetered), VPN itself is marked as always metered.
         assertTrue(mCm.isActiveNetworkMetered());
 
+
         // VPN explicitly declares WiFi as its underlying network.
         mService.setUnderlyingNetworksForVpn(
                 new Network[] { mWiFiNetworkAgent.getNetwork() });
@@ -6389,6 +6493,189 @@
         mCm.unregisterNetworkCallback(defaultCallback);
     }
 
+    private void expectNetworkRejectNonSecureVpn(InOrder inOrder, boolean add,
+            UidRangeParcel... expected) throws Exception {
+        inOrder.verify(mMockNetd).networkRejectNonSecureVpn(eq(add), aryEq(expected));
+    }
+
+    private void checkNetworkInfo(NetworkInfo ni, int type, DetailedState state) {
+        assertNotNull(ni);
+        assertEquals(type, ni.getType());
+        assertEquals(ConnectivityManager.getNetworkTypeName(type), state, ni.getDetailedState());
+    }
+
+    private void assertActiveNetworkInfo(int type, DetailedState state) {
+        checkNetworkInfo(mCm.getActiveNetworkInfo(), type, state);
+    }
+    private void assertNetworkInfo(int type, DetailedState state) {
+        checkNetworkInfo(mCm.getNetworkInfo(type), type, state);
+    }
+
+    @Test
+    public void testNetworkBlockedStatusAlwaysOnVpn() throws Exception {
+        mServiceContext.setPermission(
+                Manifest.permission.CONTROL_ALWAYS_ON_VPN, PERMISSION_GRANTED);
+        mServiceContext.setPermission(
+                Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED);
+        mServiceContext.setPermission(
+                Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
+
+        final TestNetworkCallback callback = new TestNetworkCallback();
+        final NetworkRequest request = new NetworkRequest.Builder()
+                .removeCapability(NET_CAPABILITY_NOT_VPN)
+                .build();
+        mCm.registerNetworkCallback(request, callback);
+
+        final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+        mCm.registerDefaultNetworkCallback(defaultCallback);
+
+        final TestNetworkCallback vpnUidCallback = new TestNetworkCallback();
+        final NetworkRequest vpnUidRequest = new NetworkRequest.Builder().build();
+        registerNetworkCallbackAsUid(vpnUidRequest, vpnUidCallback, VPN_UID);
+
+        final int uid = Process.myUid();
+        final int userId = UserHandle.getUserId(uid);
+        final ArrayList<String> allowList = new ArrayList<>();
+        mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
+
+        UidRangeParcel firstHalf = new UidRangeParcel(1, VPN_UID - 1);
+        UidRangeParcel secondHalf = new UidRangeParcel(VPN_UID + 1, 99999);
+        InOrder inOrder = inOrder(mMockNetd);
+        expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf);
+
+        // Connect a network when lockdown is active, expect to see it blocked.
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(false /* validated */);
+        callback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent);
+        defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent);
+        vpnUidCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
+        assertNull(mCm.getActiveNetwork());
+        assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
+        // Mobile is BLOCKED even though it's not actually connected.
+        assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+        assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
+
+        // Disable lockdown, expect to see the network unblocked.
+        // There are no callbacks because they are not implemented yet.
+        mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+        expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf);
+        vpnUidCallback.assertNoCallback();
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+        assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+        assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
+        assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+
+        // Add our UID to the allowlist and re-enable lockdown, expect network is not blocked.
+        allowList.add(TEST_PACKAGE_NAME);
+        mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
+        callback.assertNoCallback();
+        defaultCallback.assertNoCallback();
+        vpnUidCallback.assertNoCallback();
+
+        // The following requires that the UID of this test package is greater than VPN_UID. This
+        // is always true in practice because a plain AOSP build with no apps installed has almost
+        // 200 packages installed.
+        final UidRangeParcel piece1 = new UidRangeParcel(1, VPN_UID - 1);
+        final UidRangeParcel piece2 = new UidRangeParcel(VPN_UID + 1, uid - 1);
+        final UidRangeParcel piece3 = new UidRangeParcel(uid + 1, 99999);
+        expectNetworkRejectNonSecureVpn(inOrder, true, piece1, piece2, piece3);
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+        assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+        assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
+        assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+
+        // Connect a new network, expect it to be unblocked.
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(false /* validated */);
+        callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+        defaultCallback.assertNoCallback();
+        vpnUidCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+        assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+        // Cellular is DISCONNECTED because it's not the default and there are no requests for it.
+        assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
+        assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+
+        // Disable lockdown, remove our UID from the allowlist, and re-enable lockdown.
+        // Everything should now be blocked.
+        mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+        expectNetworkRejectNonSecureVpn(inOrder, false, piece1, piece2, piece3);
+        allowList.clear();
+        mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
+        expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf);
+        vpnUidCallback.assertNoCallback();
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
+        assertNull(mCm.getActiveNetwork());
+        assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
+        assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+        assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
+
+        // Disable lockdown. Everything is unblocked.
+        mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+        vpnUidCallback.assertNoCallback();
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+        assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+        assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
+        assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+
+        // Enable and disable an always-on VPN package without lockdown. Expect no changes.
+        reset(mMockNetd);
+        mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, false /* lockdown */, allowList);
+        inOrder.verify(mMockNetd, never()).networkRejectNonSecureVpn(anyBoolean(), any());
+        callback.assertNoCallback();
+        defaultCallback.assertNoCallback();
+        vpnUidCallback.assertNoCallback();
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+        assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+        assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
+        assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+
+        mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+        inOrder.verify(mMockNetd, never()).networkRejectNonSecureVpn(anyBoolean(), any());
+        callback.assertNoCallback();
+        defaultCallback.assertNoCallback();
+        vpnUidCallback.assertNoCallback();
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+        assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+        assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
+        assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+
+        // Enable lockdown and connect a VPN. The VPN is not blocked.
+        mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
+        vpnUidCallback.assertNoCallback();
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
+        assertNull(mCm.getActiveNetwork());
+        assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
+        assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+        assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
+
+        mMockVpn.establishForMyUid();
+        defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+        vpnUidCallback.assertNoCallback();  // vpnUidCallback has NOT_VPN capability.
+        assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
+        assertEquals(null, mCm.getActiveNetworkForUid(VPN_UID));  // BUG?
+        assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+        assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
+        assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+        assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+
+        mMockVpn.disconnect();
+        defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+        defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent);
+        assertNull(mCm.getActiveNetwork());
+
+        mCm.unregisterNetworkCallback(callback);
+        mCm.unregisterNetworkCallback(defaultCallback);
+        mCm.unregisterNetworkCallback(vpnUidCallback);
+    }
+
     @Test
     public final void testLoseTrusted() throws Exception {
         final NetworkRequest trustedRequest = new NetworkRequest.Builder()
@@ -7822,7 +8109,16 @@
 
     @Test
     public void testDumpDoesNotCrash() {
-        StringWriter stringWriter = new StringWriter();
+        // Filing a couple requests prior to testing the dump.
+        final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback();
+        final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback();
+        final NetworkRequest genericRequest = new NetworkRequest.Builder()
+                .clearCapabilities().build();
+        final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_WIFI).build();
+        mCm.registerNetworkCallback(genericRequest, genericNetworkCallback);
+        mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
+        final StringWriter stringWriter = new StringWriter();
 
         mService.dump(new FileDescriptor(), new PrintWriter(stringWriter), new String[0]);
 
@@ -7844,11 +8140,11 @@
         mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
         mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
 
-        ConnectivityService.NetworkRequestInfo[] nriOutput = mService.requestsSortedById();
+        final ConnectivityService.NetworkRequestInfo[] nriOutput = mService.requestsSortedById();
 
         assertTrue(nriOutput.length > 1);
         for (int i = 0; i < nriOutput.length - 1; i++) {
-            boolean isRequestIdInOrder =
+            final boolean isRequestIdInOrder =
                     nriOutput[i].mRequests.get(0).requestId
                             < nriOutput[i + 1].mRequests.get(0).requestId;
             assertTrue(isRequestIdInOrder);
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 6e380be..cc47317 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -228,7 +228,6 @@
                         R.string.config_customVpnAlwaysOnDisconnectedDialogComponent));
         when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS))
                 .thenReturn(true);
-        when(mSystemServices.isCallerSystem()).thenReturn(true);
 
         // Used by {@link Notification.Builder}
         ApplicationInfo applicationInfo = new ApplicationInfo();
@@ -1102,6 +1101,11 @@
         }
 
         @Override
+        public boolean isCallerSystem() {
+            return true;
+        }
+
+        @Override
         public void startService(final String serviceName) {
             mRunningServices.put(serviceName, true);
         }