Merge "Verify kernel implementation of AES-XCBC"
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 8adcbd9..36a2f10 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -48,6 +48,7 @@
 import android.net.util.SharedLog;
 import android.net.util.TetheringUtils.ForwardedStats;
 import android.os.Handler;
+import android.os.SystemClock;
 import android.system.ErrnoException;
 import android.text.TextUtils;
 import android.util.Log;
@@ -773,6 +774,12 @@
         }
         pw.decreaseIndent();
 
+        pw.println("BPF stats:");
+        pw.increaseIndent();
+        dumpBpfStats(pw);
+        pw.decreaseIndent();
+        pw.println();
+
         pw.println("Forwarding rules:");
         pw.increaseIndent();
         dumpIpv6UpstreamRules(pw);
@@ -800,6 +807,22 @@
                     upstreamIfindex), stats.toString()));
         }
     }
+    private void dumpBpfStats(@NonNull IndentingPrintWriter pw) {
+        try (BpfMap<TetherStatsKey, TetherStatsValue> map = mDeps.getBpfStatsMap()) {
+            if (map == null) {
+                pw.println("No BPF stats map");
+                return;
+            }
+            if (map.isEmpty()) {
+                pw.println("<empty>");
+            }
+            map.forEach((k, v) -> {
+                pw.println(String.format("%s: %s", k, v));
+            });
+        } catch (ErrnoException e) {
+            pw.println("Error dumping BPF stats map: " + e);
+        }
+    }
 
     private void dumpIpv6ForwardingRules(@NonNull IndentingPrintWriter pw) {
         if (mIpv6ForwardingRules.size() == 0) {
@@ -849,7 +872,7 @@
         }
     }
 
-    private String ipv4RuleToString(Tether4Key key, Tether4Value value) {
+    private String ipv4RuleToString(long now, Tether4Key key, Tether4Value value) {
         final String private4, public4, dst4;
         try {
             private4 = InetAddress.getByAddress(key.src4).getHostAddress();
@@ -858,29 +881,43 @@
         } catch (UnknownHostException impossible) {
             throw new AssertionError("4-byte array not valid IPv4 address!");
         }
-        return String.format("[%s] %d(%s) %s:%d -> %d(%s) %s:%d -> %s:%d",
+        long ageMs = (now - value.lastUsed) / 1_000_000;
+        return String.format("[%s] %d(%s) %s:%d -> %d(%s) %s:%d -> %s:%d %dms",
                 key.dstMac, key.iif, getIfName(key.iif), private4, key.srcPort,
                 value.oif, getIfName(value.oif),
-                public4, value.srcPort, dst4, key.dstPort);
+                public4, value.srcPort, dst4, key.dstPort, ageMs);
+    }
+
+    private void dumpIpv4ForwardingRuleMap(long now, BpfMap<Tether4Key, Tether4Value> map,
+            IndentingPrintWriter pw) throws ErrnoException {
+        if (map == null) {
+            pw.println("No IPv4 support");
+            return;
+        }
+        if (map.isEmpty()) {
+            pw.println("No rules");
+            return;
+        }
+        map.forEach((k, v) -> pw.println(ipv4RuleToString(now, k, v)));
     }
 
     private void dumpIpv4ForwardingRules(IndentingPrintWriter pw) {
-        try (BpfMap<Tether4Key, Tether4Value> map = mDeps.getBpfUpstream4Map()) {
-            if (map == null) {
-                pw.println("No IPv4 support");
-                return;
-            }
-            if (map.isEmpty()) {
-                pw.println("No IPv4 rules");
-                return;
-            }
-            pw.println("IPv4: [inDstMac] iif(iface) src -> nat -> dst");
+        final long now = SystemClock.elapsedRealtimeNanos();
+
+        try (BpfMap<Tether4Key, Tether4Value> upstreamMap = mDeps.getBpfUpstream4Map();
+                BpfMap<Tether4Key, Tether4Value> downstreamMap = mDeps.getBpfDownstream4Map()) {
+            pw.println("IPv4 Upstream: [inDstMac] iif(iface) src -> nat -> dst");
             pw.increaseIndent();
-            map.forEach((k, v) -> pw.println(ipv4RuleToString(k, v)));
+            dumpIpv4ForwardingRuleMap(now, upstreamMap, pw);
+            pw.decreaseIndent();
+
+            pw.println("IPv4 Downstream: [inDstMac] iif(iface) src -> nat -> dst");
+            pw.increaseIndent();
+            dumpIpv4ForwardingRuleMap(now, downstreamMap, pw);
+            pw.decreaseIndent();
         } catch (ErrnoException e) {
             pw.println("Error dumping IPv4 map: " + e);
         }
-        pw.decreaseIndent();
     }
 
     /**
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index 90d821b..82842bc 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -817,6 +817,7 @@
         final int originalOwnerUid = getOwnerUid();
         final int[] originalAdministratorUids = getAdministratorUids();
         final TransportInfo originalTransportInfo = getTransportInfo();
+        final Set<Integer> originalSubIds = getSubscriptionIds();
         clearAll();
         if (0 != (originalCapabilities & NET_CAPABILITY_NOT_RESTRICTED)) {
             // If the test network is not restricted, then it is only allowed to declare some
@@ -825,6 +826,9 @@
             mTransportTypes =
                     (originalTransportTypes & UNRESTRICTED_TEST_NETWORKS_ALLOWED_TRANSPORTS)
                             | (1 << TRANSPORT_TEST);
+
+            // SubIds are only allowed for Test Networks that only declare TRANSPORT_TEST.
+            setSubscriptionIds(originalSubIds);
         } else {
             // If the test transport is restricted, then it may declare any transport.
             mTransportTypes = (originalTransportTypes | (1 << TRANSPORT_TEST));
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 15b666a..913b3f3 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -398,6 +398,32 @@
     }
 
     /**
+     * The priority value is used when issue uid ranges rules to netd. Netd will use the priority
+     * value and uid ranges to generate corresponding ip rules specific to the given preference.
+     * Thus, any device originated data traffic of the applied uids can be routed to the altered
+     * default network which has highest priority.
+     *
+     * Note: The priority value should be in 0~1000. Larger value means lower priority, see
+     *       {@link NativeUidRangeConfig}.
+     */
+    // This is default priority value for those NetworkRequests which doesn't have preference to
+    // alter default network and use the global one.
+    @VisibleForTesting
+    static final int DEFAULT_NETWORK_PRIORITY_NONE = 0;
+    // Used by automotive devices to set the network preferences used to direct traffic at an
+    // application level. See {@link #setOemNetworkPreference}.
+    @VisibleForTesting
+    static final int DEFAULT_NETWORK_PRIORITY_OEM = 10;
+    // Request that a user profile is put by default on a network matching a given preference.
+    // See {@link #setProfileNetworkPreference}.
+    @VisibleForTesting
+    static final int DEFAULT_NETWORK_PRIORITY_PROFILE = 20;
+    // Set by MOBILE_DATA_PREFERRED_UIDS setting. Use mobile data in preference even when
+    // higher-priority networks are connected.
+    @VisibleForTesting
+    static final int DEFAULT_NETWORK_PRIORITY_MOBILE_DATA_PREFERRED = 30;
+
+    /**
      * used internally to clear a wakelock when transitioning
      * from one net to another.  Clear happens when we get a new
      * network - EVENT_EXPIRE_NET_TRANSITION_WAKELOCK happens
@@ -1516,7 +1542,7 @@
     }
 
     // Note that registering observer for setting do not get initial callback when registering,
-    // callers might have self-initialization to update status if need.
+    // callers must fetch the initial value of the setting themselves if needed.
     private void registerSettingsCallbacks() {
         // Watch for global HTTP proxy changes.
         mSettingsObserver.observe(
@@ -4146,8 +4172,10 @@
             final NetworkAgentInfo satisfier = nri.getSatisfier();
             if (null != satisfier) {
                 try {
+                    // TODO: Passing default network priority to netd.
                     mNetd.networkRemoveUidRanges(satisfier.network.getNetId(),
-                            toUidRangeStableParcels(nri.getUids()));
+                            toUidRangeStableParcels(nri.getUids())
+                            /* nri.getDefaultNetworkPriority() */);
                 } catch (RemoteException e) {
                     loge("Exception setting network preference default network", e);
                 }
@@ -5592,6 +5620,13 @@
         // maximum limit of registered callbacks per UID.
         final int mAsUid;
 
+        // Default network priority of this request.
+        private final int mDefaultNetworkPriority;
+
+        int getDefaultNetworkPriority() {
+            return mDefaultNetworkPriority;
+        }
+
         // In order to preserve the mapping of NetworkRequest-to-callback when apps register
         // callbacks using a returned NetworkRequest, the original NetworkRequest needs to be
         // maintained for keying off of. This is only a concern when the original nri
@@ -5621,12 +5656,13 @@
 
         NetworkRequestInfo(int asUid, @NonNull final NetworkRequest r,
                 @Nullable final PendingIntent pi, @Nullable String callingAttributionTag) {
-            this(asUid, Collections.singletonList(r), r, pi, callingAttributionTag);
+            this(asUid, Collections.singletonList(r), r, pi, callingAttributionTag,
+                    DEFAULT_NETWORK_PRIORITY_NONE);
         }
 
         NetworkRequestInfo(int asUid, @NonNull final List<NetworkRequest> r,
                 @NonNull final NetworkRequest requestForCallback, @Nullable final PendingIntent pi,
-                @Nullable String callingAttributionTag) {
+                @Nullable String callingAttributionTag, final int defaultNetworkPriority) {
             ensureAllNetworkRequestsHaveType(r);
             mRequests = initializeRequests(r);
             mNetworkRequestForCallback = requestForCallback;
@@ -5644,6 +5680,7 @@
              */
             mCallbackFlags = NetworkCallback.FLAG_NONE;
             mCallingAttributionTag = callingAttributionTag;
+            mDefaultNetworkPriority = defaultNetworkPriority;
         }
 
         NetworkRequestInfo(int asUid, @NonNull final NetworkRequest r, @Nullable final Messenger m,
@@ -5673,6 +5710,7 @@
             mPerUidCounter.incrementCountOrThrow(mUid);
             mCallbackFlags = callbackFlags;
             mCallingAttributionTag = callingAttributionTag;
+            mDefaultNetworkPriority = DEFAULT_NETWORK_PRIORITY_NONE;
             linkDeathRecipient();
         }
 
@@ -5712,15 +5750,18 @@
             mPerUidCounter.incrementCountOrThrow(mUid);
             mCallbackFlags = nri.mCallbackFlags;
             mCallingAttributionTag = nri.mCallingAttributionTag;
+            mDefaultNetworkPriority = DEFAULT_NETWORK_PRIORITY_NONE;
             linkDeathRecipient();
         }
 
         NetworkRequestInfo(int asUid, @NonNull final NetworkRequest r) {
-            this(asUid, Collections.singletonList(r));
+            this(asUid, Collections.singletonList(r), DEFAULT_NETWORK_PRIORITY_NONE);
         }
 
-        NetworkRequestInfo(int asUid, @NonNull final List<NetworkRequest> r) {
-            this(asUid, r, r.get(0), null /* pi */, null /* callingAttributionTag */);
+        NetworkRequestInfo(int asUid, @NonNull final List<NetworkRequest> r,
+                final int defaultNetworkPriority) {
+            this(asUid, r, r.get(0), null /* pi */, null /* callingAttributionTag */,
+                    defaultNetworkPriority);
         }
 
         // True if this NRI is being satisfied. It also accounts for if the nri has its satisifer
@@ -7377,9 +7418,13 @@
         maybeCloseSockets(nai, ranges, exemptUids);
         try {
             if (add) {
-                mNetd.networkAddUidRanges(nai.network.netId, ranges);
+                // TODO: Passing default network priority to netd.
+                mNetd.networkAddUidRanges(nai.network.netId, ranges
+                        /* DEFAULT_NETWORK_PRIORITY_NONE */);
             } else {
-                mNetd.networkRemoveUidRanges(nai.network.netId, ranges);
+                // TODO: Passing default network priority to netd.
+                mNetd.networkRemoveUidRanges(nai.network.netId, ranges
+                        /* DEFAULT_NETWORK_PRIORITY_NONE */);
             }
         } catch (Exception e) {
             loge("Exception while " + (add ? "adding" : "removing") + " uid ranges " + uidRanges +
@@ -7690,14 +7735,18 @@
                         + " any applications to set as the default." + nri);
             }
             if (null != newDefaultNetwork) {
+                // TODO: Passing default network priority to netd.
                 mNetd.networkAddUidRanges(
                         newDefaultNetwork.network.getNetId(),
-                        toUidRangeStableParcels(nri.getUids()));
+                        toUidRangeStableParcels(nri.getUids())
+                        /* nri.getDefaultNetworkPriority() */);
             }
             if (null != oldDefaultNetwork) {
+                // TODO: Passing default network priority to netd.
                 mNetd.networkRemoveUidRanges(
                         oldDefaultNetwork.network.getNetId(),
-                        toUidRangeStableParcels(nri.getUids()));
+                        toUidRangeStableParcels(nri.getUids())
+                        /* nri.getDefaultNetworkPriority() */);
             }
         } catch (RemoteException | ServiceSpecificException e) {
             loge("Exception setting app default network", e);
@@ -9755,7 +9804,8 @@
             nrs.add(createDefaultInternetRequestForTransport(
                     TYPE_NONE, NetworkRequest.Type.TRACK_DEFAULT));
             setNetworkRequestUids(nrs, UidRange.fromIntRanges(pref.capabilities.getUids()));
-            final NetworkRequestInfo nri = new NetworkRequestInfo(Process.myUid(), nrs);
+            final NetworkRequestInfo nri = new NetworkRequestInfo(Process.myUid(), nrs,
+                    DEFAULT_NETWORK_PRIORITY_PROFILE);
             result.add(nri);
         }
         return result;
@@ -9825,7 +9875,8 @@
             ranges.add(new UidRange(uid, uid));
         }
         setNetworkRequestUids(requests, ranges);
-        nris.add(new NetworkRequestInfo(Process.myUid(), requests));
+        nris.add(new NetworkRequestInfo(Process.myUid(), requests,
+                DEFAULT_NETWORK_PRIORITY_MOBILE_DATA_PREFERRED));
         return nris;
     }
 
@@ -10069,7 +10120,7 @@
 
         private SparseArray<Set<Integer>> createUidsFromOemNetworkPreferences(
                 @NonNull final OemNetworkPreferences preference) {
-            final SparseArray<Set<Integer>> uids = new SparseArray<>();
+            final SparseArray<Set<Integer>> prefToUids = new SparseArray<>();
             final PackageManager pm = mContext.getPackageManager();
             final List<UserHandle> users =
                     mContext.getSystemService(UserManager.class).getUserHandles(true);
@@ -10077,29 +10128,29 @@
                 if (VDBG || DDBG) {
                     log("No users currently available for setting the OEM network preference.");
                 }
-                return uids;
+                return prefToUids;
             }
             for (final Map.Entry<String, Integer> entry :
                     preference.getNetworkPreferences().entrySet()) {
                 @OemNetworkPreferences.OemNetworkPreference final int pref = entry.getValue();
-                try {
-                    final int uid = pm.getApplicationInfo(entry.getKey(), 0).uid;
-                    if (!uids.contains(pref)) {
-                        uids.put(pref, new ArraySet<>());
+                // Add the rules for all users as this policy is device wide.
+                for (final UserHandle user : users) {
+                    try {
+                        final int uid = pm.getApplicationInfoAsUser(entry.getKey(), 0, user).uid;
+                        if (!prefToUids.contains(pref)) {
+                            prefToUids.put(pref, new ArraySet<>());
+                        }
+                        prefToUids.get(pref).add(uid);
+                    } catch (PackageManager.NameNotFoundException e) {
+                        // Although this may seem like an error scenario, it is ok that uninstalled
+                        // packages are sent on a network preference as the system will watch for
+                        // package installations associated with this network preference and update
+                        // accordingly. This is done to minimize race conditions on app install.
+                        continue;
                     }
-                    for (final UserHandle ui : users) {
-                        // Add the rules for all users as this policy is device wide.
-                        uids.get(pref).add(ui.getUid(uid));
-                    }
-                } catch (PackageManager.NameNotFoundException e) {
-                    // Although this may seem like an error scenario, it is ok that uninstalled
-                    // packages are sent on a network preference as the system will watch for
-                    // package installations associated with this network preference and update
-                    // accordingly. This is done so as to minimize race conditions on app install.
-                    continue;
                 }
             }
-            return uids;
+            return prefToUids;
         }
 
         private NetworkRequestInfo createNriFromOemNetworkPreferences(
@@ -10135,7 +10186,8 @@
                 ranges.add(new UidRange(uid, uid));
             }
             setNetworkRequestUids(requests, ranges);
-            return new NetworkRequestInfo(Process.myUid(), requests);
+            return new NetworkRequestInfo(
+                    Process.myUid(), requests, DEFAULT_NETWORK_PRIORITY_OEM);
         }
 
         private NetworkRequest createUnmeteredNetworkRequest() {
diff --git a/service/src/com/android/server/TestNetworkService.java b/service/src/com/android/server/TestNetworkService.java
index 09873f4..fffd2be 100644
--- a/service/src/com/android/server/TestNetworkService.java
+++ b/service/src/com/android/server/TestNetworkService.java
@@ -48,7 +48,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.net.module.util.NetdUtils;
 import com.android.net.module.util.NetworkStackConstants;
-import com.android.net.module.util.PermissionUtils;
 
 import java.io.UncheckedIOException;
 import java.net.Inet4Address;
@@ -123,6 +122,8 @@
                         addr.getPrefixLength());
             }
 
+            NetdUtils.setInterfaceUp(mNetd, iface);
+
             return new TestNetworkInterface(tunIntf, iface);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -324,14 +325,6 @@
         }
 
         try {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                PermissionUtils.enforceNetworkStackPermission(mContext);
-                NetdUtils.setInterfaceUp(mNetd, iface);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-
             // Synchronize all accesses to mTestNetworkTracker to prevent the case where:
             // 1. TestNetworkAgent successfully binds to death of binder
             // 2. Before it is added to the mTestNetworkTracker, binder dies, binderDied() is called
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index 1afbfb0..7d30056 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -106,6 +106,8 @@
 
     private static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
 
+    private static final String EMPTY_STRING = "";
+
     protected static final int TYPE_COMPONENT_ACTIVTIY = 0;
     protected static final int TYPE_COMPONENT_FOREGROUND_SERVICE = 1;
 
@@ -206,6 +208,8 @@
                 final String resultData = getResultData();
                 if (resultData == null) {
                     Log.e(TAG, "Received null data from ordered intent");
+                    // Offer an empty string so that the code waiting for the result can return.
+                    result.offer(EMPTY_STRING);
                     return;
                 }
                 result.offer(resultData);
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 0075b54..1d84a1b 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -151,6 +151,7 @@
 import com.android.networkstack.apishim.common.ConnectivityManagerShim;
 import com.android.testutils.CompatUtil;
 import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 import com.android.testutils.DevSdkIgnoreRuleKt;
 import com.android.testutils.RecorderCallback.CallbackEntry;
 import com.android.testutils.SkipPresubmit;
@@ -660,6 +661,31 @@
                 .build();
     }
 
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testIsPrivateDnsBroken() throws InterruptedException {
+        final String invalidPrivateDnsServer = "invalidhostname.example.com";
+        final String goodPrivateDnsServer = "dns.google";
+        mCtsNetUtils.storePrivateDnsSetting();
+        final TestableNetworkCallback cb = new TestableNetworkCallback();
+        mCm.registerNetworkCallback(makeWifiNetworkRequest(), cb);
+        try {
+            // Verifying the good private DNS sever
+            mCtsNetUtils.setPrivateDnsStrictMode(goodPrivateDnsServer);
+            final Network networkForPrivateDns =  mCtsNetUtils.ensureWifiConnected();
+            cb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED, NETWORK_CALLBACK_TIMEOUT_MS,
+                    entry -> (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
+                    .isPrivateDnsBroken()) && networkForPrivateDns.equals(entry.getNetwork()));
+
+            // Verifying the broken private DNS sever
+            mCtsNetUtils.setPrivateDnsStrictMode(invalidPrivateDnsServer);
+            cb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED, NETWORK_CALLBACK_TIMEOUT_MS,
+                    entry -> (((CallbackEntry.CapabilitiesChanged) entry).getCaps()
+                    .isPrivateDnsBroken()) && networkForPrivateDns.equals(entry.getNetwork()));
+        } finally {
+            mCtsNetUtils.restorePrivateDnsSetting();
+        }
+    }
+
     /**
      * Exercises both registerNetworkCallback and unregisterNetworkCallback. This checks to
      * see if we get a callback for the TRANSPORT_WIFI transport type being available.
diff --git a/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java b/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
index 899295a..6bd2bd5 100644
--- a/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
+++ b/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
@@ -24,8 +24,8 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -52,6 +52,7 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class NetworkStatsManagerTest {
+    private static final String TEST_SUBSCRIBER_ID = "subid";
 
     private @Mock INetworkStatsService mService;
     private @Mock INetworkStatsSession mStatsSession;
@@ -69,7 +70,6 @@
 
     @Test
     public void testQueryDetails() throws RemoteException {
-        final String subscriberId = "subid";
         final long startTime = 1;
         final long endTime = 100;
         final int uid1 = 10001;
@@ -113,7 +113,7 @@
                 .then((InvocationOnMock inv) -> {
                     NetworkTemplate template = inv.getArgument(0);
                     assertEquals(MATCH_MOBILE_ALL, template.getMatchRule());
-                    assertEquals(subscriberId, template.getSubscriberId());
+                    assertEquals(TEST_SUBSCRIBER_ID, template.getSubscriberId());
                     return history1;
                 });
 
@@ -124,13 +124,13 @@
                 .then((InvocationOnMock inv) -> {
                     NetworkTemplate template = inv.getArgument(0);
                     assertEquals(MATCH_MOBILE_ALL, template.getMatchRule());
-                    assertEquals(subscriberId, template.getSubscriberId());
+                    assertEquals(TEST_SUBSCRIBER_ID, template.getSubscriberId());
                     return history2;
                 });
 
 
         NetworkStats stats = mManager.queryDetails(
-                ConnectivityManager.TYPE_MOBILE, subscriberId, startTime, endTime);
+                ConnectivityManager.TYPE_MOBILE, TEST_SUBSCRIBER_ID, startTime, endTime);
 
         NetworkStats.Bucket bucket = new NetworkStats.Bucket();
 
@@ -166,35 +166,30 @@
         assertFalse(stats.hasNextBucket());
     }
 
-    @Test
-    public void testQueryDetails_NoSubscriberId() throws RemoteException {
+    private void runQueryDetailsAndCheckTemplate(int networkType, String subscriberId,
+            NetworkTemplate expectedTemplate) throws RemoteException {
         final long startTime = 1;
         final long endTime = 100;
         final int uid1 = 10001;
         final int uid2 = 10002;
 
+        reset(mStatsSession);
         when(mService.openSessionForUsageStats(anyInt(), anyString())).thenReturn(mStatsSession);
         when(mStatsSession.getRelevantUids()).thenReturn(new int[] { uid1, uid2 });
-
-        NetworkStats stats = mManager.queryDetails(
-                ConnectivityManager.TYPE_MOBILE, null, startTime, endTime);
-
         when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class),
                 anyInt(), anyInt(), anyInt(), anyInt(), anyLong(), anyLong()))
                 .thenReturn(new NetworkStatsHistory(10, 0));
+        NetworkStats stats = mManager.queryDetails(
+                networkType, subscriberId, startTime, endTime);
 
         verify(mStatsSession, times(1)).getHistoryIntervalForUid(
-                argThat((NetworkTemplate t) ->
-                        // No subscriberId: MATCH_MOBILE_WILDCARD template
-                        t.getMatchRule() == NetworkTemplate.MATCH_MOBILE_WILDCARD),
+                eq(expectedTemplate),
                 eq(uid1), eq(android.net.NetworkStats.SET_ALL),
                 eq(android.net.NetworkStats.TAG_NONE),
                 eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime));
 
         verify(mStatsSession, times(1)).getHistoryIntervalForUid(
-                argThat((NetworkTemplate t) ->
-                        // No subscriberId: MATCH_MOBILE_WILDCARD template
-                        t.getMatchRule() == NetworkTemplate.MATCH_MOBILE_WILDCARD),
+                eq(expectedTemplate),
                 eq(uid2), eq(android.net.NetworkStats.SET_ALL),
                 eq(android.net.NetworkStats.TAG_NONE),
                 eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime));
@@ -202,6 +197,25 @@
         assertFalse(stats.hasNextBucket());
     }
 
+    @Test
+    public void testNetworkTemplateWhenRunningQueryDetails_NoSubscriberId() throws RemoteException {
+        runQueryDetailsAndCheckTemplate(ConnectivityManager.TYPE_MOBILE,
+                null /* subscriberId */, NetworkTemplate.buildTemplateMobileWildcard());
+        runQueryDetailsAndCheckTemplate(ConnectivityManager.TYPE_WIFI,
+                "" /* subscriberId */, NetworkTemplate.buildTemplateWifiWildcard());
+        runQueryDetailsAndCheckTemplate(ConnectivityManager.TYPE_WIFI,
+                null /* subscriberId */, NetworkTemplate.buildTemplateWifiWildcard());
+    }
+
+    @Test
+    public void testNetworkTemplateWhenRunningQueryDetails_MergedCarrierWifi()
+            throws RemoteException {
+        runQueryDetailsAndCheckTemplate(ConnectivityManager.TYPE_WIFI,
+                TEST_SUBSCRIBER_ID,
+                NetworkTemplate.buildTemplateWifi(NetworkTemplate.WIFI_NETWORKID_ALL,
+                        TEST_SUBSCRIBER_ID));
+    }
+
     private void assertBucketMatches(Entry expected, NetworkStats.Bucket actual) {
         assertEquals(expected.uid, actual.getUid());
         assertEquals(expected.rxBytes, actual.getRxBytes());
diff --git a/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java b/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java
index fc739fb..bc6dbf2 100644
--- a/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java
+++ b/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java
@@ -24,7 +24,9 @@
 import static org.junit.Assert.fail;
 
 import android.net.util.KeepalivePacketDataUtil;
+import android.util.Log;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -38,8 +40,19 @@
     private static final byte[] IPV4_KEEPALIVE_SRC_ADDR = {10, 0, 0, 1};
     private static final byte[] IPV4_KEEPALIVE_DST_ADDR = {10, 0, 0, 5};
 
+    private Log.TerribleFailureHandler mOriginalHandler;
+
     @Before
-    public void setUp() {}
+    public void setUp() {
+        // Terrible failures are logged when using deprecated methods on newer platforms
+        mOriginalHandler = Log.setWtfHandler((tag, what, sys) ->
+                Log.e(tag, "Terrible failure in test", what));
+    }
+
+    @After
+    public void tearDown() {
+        Log.setWtfHandler(mOriginalHandler);
+    }
 
     @Test
     public void testFromTcpKeepaliveStableParcelable() throws Exception {
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index a9612de..83d2e1a 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -118,6 +118,8 @@
 import static android.os.Process.INVALID_UID;
 import static android.system.OsConstants.IPPROTO_TCP;
 
+import static com.android.server.ConnectivityService.DEFAULT_NETWORK_PRIORITY_MOBILE_DATA_PREFERRED;
+import static com.android.server.ConnectivityService.DEFAULT_NETWORK_PRIORITY_OEM;
 import static com.android.server.ConnectivityServiceTestUtils.transportToLegacyType;
 import static com.android.testutils.ConcurrentUtils.await;
 import static com.android.testutils.ConcurrentUtils.durationOf;
@@ -456,6 +458,7 @@
     private TestNetworkCallback mDefaultNetworkCallback;
     private TestNetworkCallback mSystemDefaultNetworkCallback;
     private TestNetworkCallback mProfileDefaultNetworkCallback;
+    private TestNetworkCallback mTestPackageDefaultNetworkCallback;
 
     // State variables required to emulate NetworkPolicyManagerService behaviour.
     private int mBlockedReasons = BLOCKED_REASON_NONE;
@@ -1523,6 +1526,8 @@
     }
 
     private static final int PRIMARY_USER = 0;
+    private static final int SECONDARY_USER = 10;
+    private static final int TERTIARY_USER = 11;
     private static final UidRange PRIMARY_UIDRANGE =
             UidRange.createForUser(UserHandle.of(PRIMARY_USER));
     private static final int APP1_UID = UserHandle.getUid(PRIMARY_USER, 10100);
@@ -1531,8 +1536,8 @@
     private static final UserInfo PRIMARY_USER_INFO = new UserInfo(PRIMARY_USER, "",
             UserInfo.FLAG_PRIMARY);
     private static final UserHandle PRIMARY_USER_HANDLE = new UserHandle(PRIMARY_USER);
-    private static final int SECONDARY_USER = 10;
     private static final UserHandle SECONDARY_USER_HANDLE = new UserHandle(SECONDARY_USER);
+    private static final UserHandle TERTIARY_USER_HANDLE = new UserHandle(TERTIARY_USER);
 
     private static final int RESTRICTED_USER = 1;
     private static final UserInfo RESTRICTED_USER_INFO = new UserInfo(RESTRICTED_USER, "",
@@ -10548,29 +10553,30 @@
         fail("TOO_MANY_REQUESTS never thrown");
     }
 
-    private UidRange createUidRange(int userId) {
-        return UidRange.createForUser(UserHandle.of(userId));
+    private void mockGetApplicationInfo(@NonNull final String packageName, final int uid) {
+        mockGetApplicationInfo(packageName, uid, PRIMARY_USER_HANDLE);
     }
 
-    private void mockGetApplicationInfo(@NonNull final String packageName, @NonNull final int uid) {
+    private void mockGetApplicationInfo(@NonNull final String packageName, final int uid,
+            @NonNull final UserHandle user) {
         final ApplicationInfo applicationInfo = new ApplicationInfo();
         applicationInfo.uid = uid;
         try {
-            when(mPackageManager.getApplicationInfo(eq(packageName), anyInt()))
+            when(mPackageManager.getApplicationInfoAsUser(eq(packageName), anyInt(), eq(user)))
                     .thenReturn(applicationInfo);
         } catch (Exception e) {
             fail(e.getMessage());
         }
     }
 
-    private void mockGetApplicationInfoThrowsNameNotFound(@NonNull final String packageName)
+    private void mockGetApplicationInfoThrowsNameNotFound(@NonNull final String packageName,
+            @NonNull final UserHandle user)
             throws Exception {
-        when(mPackageManager.getApplicationInfo(eq(packageName), anyInt()))
+        when(mPackageManager.getApplicationInfoAsUser(eq(packageName), anyInt(), eq(user)))
                 .thenThrow(new PackageManager.NameNotFoundException(packageName));
     }
 
-    private void mockHasSystemFeature(@NonNull final String featureName,
-            @NonNull final boolean hasFeature) {
+    private void mockHasSystemFeature(@NonNull final String featureName, final boolean hasFeature) {
         when(mPackageManager.hasSystemFeature(eq(featureName)))
                 .thenReturn(hasFeature);
     }
@@ -10618,8 +10624,9 @@
                 mService.new OemNetworkRequestFactory()
                         .createNrisFromOemNetworkPreferences(
                                 createDefaultOemNetworkPreferences(prefToTest));
-
-        final List<NetworkRequest> mRequests = nris.iterator().next().mRequests;
+        final NetworkRequestInfo nri = nris.iterator().next();
+        assertEquals(DEFAULT_NETWORK_PRIORITY_OEM, nri.getDefaultNetworkPriority());
+        final List<NetworkRequest> mRequests = nri.mRequests;
         assertEquals(expectedNumOfNris, nris.size());
         assertEquals(expectedNumOfRequests, mRequests.size());
         assertTrue(mRequests.get(0).isListen());
@@ -10647,8 +10654,9 @@
                 mService.new OemNetworkRequestFactory()
                         .createNrisFromOemNetworkPreferences(
                                 createDefaultOemNetworkPreferences(prefToTest));
-
-        final List<NetworkRequest> mRequests = nris.iterator().next().mRequests;
+        final NetworkRequestInfo nri = nris.iterator().next();
+        assertEquals(DEFAULT_NETWORK_PRIORITY_OEM, nri.getDefaultNetworkPriority());
+        final List<NetworkRequest> mRequests = nri.mRequests;
         assertEquals(expectedNumOfNris, nris.size());
         assertEquals(expectedNumOfRequests, mRequests.size());
         assertTrue(mRequests.get(0).isListen());
@@ -10673,8 +10681,9 @@
                 mService.new OemNetworkRequestFactory()
                         .createNrisFromOemNetworkPreferences(
                                 createDefaultOemNetworkPreferences(prefToTest));
-
-        final List<NetworkRequest> mRequests = nris.iterator().next().mRequests;
+        final NetworkRequestInfo nri = nris.iterator().next();
+        assertEquals(DEFAULT_NETWORK_PRIORITY_OEM, nri.getDefaultNetworkPriority());
+        final List<NetworkRequest> mRequests = nri.mRequests;
         assertEquals(expectedNumOfNris, nris.size());
         assertEquals(expectedNumOfRequests, mRequests.size());
         assertTrue(mRequests.get(0).isRequest());
@@ -10696,8 +10705,9 @@
                 mService.new OemNetworkRequestFactory()
                         .createNrisFromOemNetworkPreferences(
                                 createDefaultOemNetworkPreferences(prefToTest));
-
-        final List<NetworkRequest> mRequests = nris.iterator().next().mRequests;
+        final NetworkRequestInfo nri = nris.iterator().next();
+        assertEquals(DEFAULT_NETWORK_PRIORITY_OEM, nri.getDefaultNetworkPriority());
+        final List<NetworkRequest> mRequests = nri.mRequests;
         assertEquals(expectedNumOfNris, nris.size());
         assertEquals(expectedNumOfRequests, mRequests.size());
         assertTrue(mRequests.get(0).isRequest());
@@ -10764,16 +10774,18 @@
     }
 
     @Test
-    public void testOemNetworkRequestFactoryMultipleUsersCorrectlySetsUids()
+    public void testOemNetworkRequestFactoryMultipleUsersSetsUids()
             throws Exception {
         // Arrange users
-        final int secondUser = 10;
-        final UserHandle secondUserHandle = new UserHandle(secondUser);
+        final int secondUserTestPackageUid = UserHandle.getUid(SECONDARY_USER, TEST_PACKAGE_UID);
+        final int thirdUserTestPackageUid = UserHandle.getUid(TERTIARY_USER, TEST_PACKAGE_UID);
         when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
-                Arrays.asList(PRIMARY_USER_HANDLE, secondUserHandle));
+                Arrays.asList(PRIMARY_USER_HANDLE, SECONDARY_USER_HANDLE, TERTIARY_USER_HANDLE));
 
-        // Arrange PackageManager mocks
-        mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID);
+        // Arrange PackageManager mocks testing for users who have and don't have a package.
+        mockGetApplicationInfoThrowsNameNotFound(TEST_PACKAGE_NAME, PRIMARY_USER_HANDLE);
+        mockGetApplicationInfo(TEST_PACKAGE_NAME, secondUserTestPackageUid, SECONDARY_USER_HANDLE);
+        mockGetApplicationInfo(TEST_PACKAGE_NAME, thirdUserTestPackageUid, TERTIARY_USER_HANDLE);
 
         // Build OemNetworkPreferences object
         final int testOemPref = OEM_NETWORK_PREFERENCE_OEM_PAID;
@@ -10787,8 +10799,8 @@
                         mService.new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(
                                 pref));
 
-        // UIDs for all users and all managed packages should be present.
-        // Two users each with two packages.
+        // UIDs for users with installed packages should be present.
+        // Three users exist, but only two have the test package installed.
         final int expectedUidSize = 2;
         final List<Range<Integer>> uids =
                 new ArrayList<>(nris.get(0).mRequests.get(0).networkCapabilities.getUids());
@@ -10796,11 +10808,10 @@
 
         // Sort by uid to access nris by index
         uids.sort(Comparator.comparingInt(uid -> uid.getLower()));
-        final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID);
-        assertEquals(TEST_PACKAGE_UID, (int) uids.get(0).getLower());
-        assertEquals(TEST_PACKAGE_UID, (int) uids.get(0).getUpper());
-        assertEquals(secondUserTestPackageUid, (int) uids.get(1).getLower());
-        assertEquals(secondUserTestPackageUid, (int) uids.get(1).getUpper());
+        assertEquals(secondUserTestPackageUid, (int) uids.get(0).getLower());
+        assertEquals(secondUserTestPackageUid, (int) uids.get(0).getUpper());
+        assertEquals(thirdUserTestPackageUid, (int) uids.get(1).getLower());
+        assertEquals(thirdUserTestPackageUid, (int) uids.get(1).getUpper());
     }
 
     @Test
@@ -10961,16 +10972,24 @@
     }
 
     private void registerDefaultNetworkCallbacks() {
+        if (mSystemDefaultNetworkCallback != null || mDefaultNetworkCallback != null
+                || mProfileDefaultNetworkCallback != null
+                || mTestPackageDefaultNetworkCallback != null) {
+            throw new IllegalStateException("Default network callbacks already registered");
+        }
+
         // Using Manifest.permission.NETWORK_SETTINGS for registerSystemDefaultNetworkCallback()
         mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED);
         mSystemDefaultNetworkCallback = new TestNetworkCallback();
         mDefaultNetworkCallback = new TestNetworkCallback();
         mProfileDefaultNetworkCallback = new TestNetworkCallback();
+        mTestPackageDefaultNetworkCallback = new TestNetworkCallback();
         mCm.registerSystemDefaultNetworkCallback(mSystemDefaultNetworkCallback,
                 new Handler(ConnectivityThread.getInstanceLooper()));
         mCm.registerDefaultNetworkCallback(mDefaultNetworkCallback);
         registerDefaultNetworkCallbackAsUid(mProfileDefaultNetworkCallback,
                 TEST_WORK_PROFILE_APP_UID);
+        registerDefaultNetworkCallbackAsUid(mTestPackageDefaultNetworkCallback, TEST_PACKAGE_UID);
         // TODO: test using ConnectivityManager#registerDefaultNetworkCallbackAsUid as well.
         mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_DENIED);
     }
@@ -10985,6 +11004,9 @@
         if (null != mProfileDefaultNetworkCallback) {
             mCm.unregisterNetworkCallback(mProfileDefaultNetworkCallback);
         }
+        if (null != mTestPackageDefaultNetworkCallback) {
+            mCm.unregisterNetworkCallback(mTestPackageDefaultNetworkCallback);
+        }
     }
 
     private void setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(
@@ -11601,6 +11623,7 @@
         final UidRangeParcel[] uidRanges =
                 toUidRangeStableParcels(
                         uidRangesForUids(TEST_PACKAGE_UID, secondUserTestPackageUid));
+        mockGetApplicationInfo(TEST_PACKAGE_NAME, secondUserTestPackageUid, secondUserHandle);
         setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME);
 
         // Verify the starting state. No networks should be connected.
@@ -11643,6 +11666,7 @@
         final UidRangeParcel[] uidRangesBothUsers =
                 toUidRangeStableParcels(
                         uidRangesForUids(TEST_PACKAGE_UID, secondUserTestPackageUid));
+        mockGetApplicationInfo(TEST_PACKAGE_NAME, secondUserTestPackageUid, secondUserHandle);
         setupSetOemNetworkPreferenceForPreferenceTest(
                 networkPref, uidRangesSingleUser, TEST_PACKAGE_NAME);
 
@@ -11699,7 +11723,7 @@
         final UidRangeParcel[] uidRangesSinglePackage =
                 toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID));
         mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID);
-        mockGetApplicationInfoThrowsNameNotFound(packageToInstall);
+        mockGetApplicationInfoThrowsNameNotFound(packageToInstall, PRIMARY_USER_HANDLE);
         setOemNetworkPreference(networkPref, TEST_PACKAGE_NAME, packageToInstall);
         grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid(), packageToInstall);
 
@@ -11733,7 +11757,7 @@
                 false /* shouldDestroyNetwork */);
 
         // Set the system to no longer recognize the package to be installed
-        mockGetApplicationInfoThrowsNameNotFound(packageToInstall);
+        mockGetApplicationInfoThrowsNameNotFound(packageToInstall, PRIMARY_USER_HANDLE);
 
         // Send a broadcast indicating a package was removed.
         final Intent removedIntent = new Intent(ACTION_PACKAGE_REMOVED);
@@ -12874,6 +12898,8 @@
         assertEquals(1, nris.size());
         assertTrue(nri.isMultilayerRequest());
         assertEquals(nri.getUids(), uidRangesForUids(uids));
+        assertEquals(DEFAULT_NETWORK_PRIORITY_MOBILE_DATA_PREFERRED,
+                nri.getDefaultNetworkPriority());
     }
 
     /**
@@ -12906,9 +12932,11 @@
     @Test
     public void testMobileDataPreferredUidsChanged() throws Exception {
         final InOrder inorder = inOrder(mMockNetd);
+        registerDefaultNetworkCallbacks();
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
-        waitForIdle();
+        mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        mTestPackageDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
 
         final int cellNetId = mCellNetworkAgent.getNetwork().netId;
         inorder.verify(mMockNetd, times(1)).networkCreate(nativeNetworkConfigPhysical(
@@ -12919,12 +12947,15 @@
         inorder.verify(mMockNetd, never()).networkAddUidRanges(anyInt(), any());
         inorder.verify(mMockNetd, never()).networkRemoveUidRanges(anyInt(), any());
 
+        // Set MOBILE_DATA_PREFERRED_UIDS setting and verify that net id and uid ranges send to netd
         final Set<Integer> uids1 = Set.of(PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID));
         final UidRangeParcel[] uidRanges1 = toUidRangeStableParcels(uidRangesForUids(uids1));
         setAndUpdateMobileDataPreferredUids(uids1);
         inorder.verify(mMockNetd, times(1)).networkAddUidRanges(cellNetId, uidRanges1);
         inorder.verify(mMockNetd, never()).networkRemoveUidRanges(anyInt(), any());
 
+        // Set MOBILE_DATA_PREFERRED_UIDS setting again and verify that old rules are removed and
+        // new rules are added.
         final Set<Integer> uids2 = Set.of(PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID),
                 PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID2),
                 SECONDARY_USER_HANDLE.getUid(TEST_PACKAGE_UID));
@@ -12932,5 +12963,170 @@
         setAndUpdateMobileDataPreferredUids(uids2);
         inorder.verify(mMockNetd, times(1)).networkRemoveUidRanges(cellNetId, uidRanges1);
         inorder.verify(mMockNetd, times(1)).networkAddUidRanges(cellNetId, uidRanges2);
+
+        // Clear MOBILE_DATA_PREFERRED_UIDS setting again and verify that old rules are removed and
+        // new rules are not added.
+        final Set<Integer> uids3 = Set.of();
+        final UidRangeParcel[] uidRanges3 = toUidRangeStableParcels(uidRangesForUids(uids3));
+        setAndUpdateMobileDataPreferredUids(uids3);
+        inorder.verify(mMockNetd, times(1)).networkRemoveUidRanges(cellNetId, uidRanges2);
+        inorder.verify(mMockNetd, never()).networkAddUidRanges(anyInt(), any());
+    }
+
+    /**
+     * Make sure mobile data preferred uids feature behaves as expected when the mobile network
+     * goes up and down while the uids is set. Make sure they behave as expected whether
+     * there is a general default network or not.
+     */
+    @Test
+    public void testMobileDataPreferenceForMobileNetworkUpDown() throws Exception {
+        final InOrder inorder = inOrder(mMockNetd);
+        // File a request for cell to ensure it doesn't go down.
+        final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+        final NetworkRequest cellRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR).build();
+        mCm.requestNetwork(cellRequest, cellNetworkCallback);
+        cellNetworkCallback.assertNoCallback();
+
+        registerDefaultNetworkCallbacks();
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(true);
+        mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+        mTestPackageDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(TEST_PACKAGE_UID));
+
+        final int wifiNetId = mWiFiNetworkAgent.getNetwork().netId;
+        inorder.verify(mMockNetd, times(1)).networkCreate(nativeNetworkConfigPhysical(
+                wifiNetId, INetd.PERMISSION_NONE));
+
+        // Initial mobile data preferred uids status.
+        setAndUpdateMobileDataPreferredUids(Set.of());
+        inorder.verify(mMockNetd, never()).networkAddUidRanges(anyInt(), any());
+        inorder.verify(mMockNetd, never()).networkRemoveUidRanges(anyInt(), any());
+
+        // Set MOBILE_DATA_PREFERRED_UIDS setting and verify that wifi net id and uid ranges send to
+        // netd.
+        final Set<Integer> uids = Set.of(PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID));
+        final UidRangeParcel[] uidRanges = toUidRangeStableParcels(uidRangesForUids(uids));
+        setAndUpdateMobileDataPreferredUids(uids);
+        inorder.verify(mMockNetd, times(1)).networkAddUidRanges(wifiNetId, uidRanges);
+        inorder.verify(mMockNetd, never()).networkRemoveUidRanges(anyInt(), any());
+
+        // Cellular network connected. mTestPackageDefaultNetworkCallback should receive
+        // callback with cellular network and net id and uid ranges should be updated to netd.
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        mDefaultNetworkCallback.assertNoCallback();
+        mTestPackageDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(TEST_PACKAGE_UID));
+
+        final int cellNetId = mCellNetworkAgent.getNetwork().netId;
+        inorder.verify(mMockNetd, times(1)).networkCreate(nativeNetworkConfigPhysical(
+                cellNetId, INetd.PERMISSION_NONE));
+        inorder.verify(mMockNetd, times(1)).networkAddUidRanges(cellNetId, uidRanges);
+        inorder.verify(mMockNetd, times(1)).networkRemoveUidRanges(wifiNetId, uidRanges);
+
+        // Cellular network disconnected. mTestPackageDefaultNetworkCallback should receive
+        // callback with wifi network from fallback request.
+        mCellNetworkAgent.disconnect();
+        mDefaultNetworkCallback.assertNoCallback();
+        cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        mTestPackageDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        mTestPackageDefaultNetworkCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(TEST_PACKAGE_UID));
+        inorder.verify(mMockNetd, times(1)).networkAddUidRanges(wifiNetId, uidRanges);
+        inorder.verify(mMockNetd, never()).networkRemoveUidRanges(anyInt(), any());
+        inorder.verify(mMockNetd).networkDestroy(cellNetId);
+
+        // Cellular network comes back. mTestPackageDefaultNetworkCallback should receive
+        // callback with cellular network.
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        mDefaultNetworkCallback.assertNoCallback();
+        mTestPackageDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(TEST_PACKAGE_UID));
+
+        final int cellNetId2 = mCellNetworkAgent.getNetwork().netId;
+        inorder.verify(mMockNetd, times(1)).networkCreate(nativeNetworkConfigPhysical(
+                cellNetId2, INetd.PERMISSION_NONE));
+        inorder.verify(mMockNetd, times(1)).networkAddUidRanges(cellNetId2, uidRanges);
+        inorder.verify(mMockNetd, times(1)).networkRemoveUidRanges(wifiNetId, uidRanges);
+
+        // Wifi network disconnected. mTestPackageDefaultNetworkCallback should not receive
+        // any callback.
+        mWiFiNetworkAgent.disconnect();
+        mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+        mTestPackageDefaultNetworkCallback.assertNoCallback();
+        assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(TEST_PACKAGE_UID));
+        waitForIdle();
+        inorder.verify(mMockNetd, never()).networkAddUidRanges(anyInt(), any());
+        inorder.verify(mMockNetd, never()).networkRemoveUidRanges(anyInt(), any());
+        inorder.verify(mMockNetd).networkDestroy(wifiNetId);
+
+        mCm.unregisterNetworkCallback(cellNetworkCallback);
+    }
+
+    @Test
+    public void testSetMobileDataPreferredUids_noIssueToFactory() throws Exception {
+        // First set mobile data preferred uid to create a multi-layer requests: 1. listen for
+        // cellular, 2. track the default network for fallback.
+        setAndUpdateMobileDataPreferredUids(
+                Set.of(PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID)));
+
+        final HandlerThread handlerThread = new HandlerThread("MockFactory");
+        handlerThread.start();
+        NetworkCapabilities internetFilter = new NetworkCapabilities()
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
+        final MockNetworkFactory internetFactory = new MockNetworkFactory(handlerThread.getLooper(),
+                mServiceContext, "internetFactory", internetFilter, mCsHandlerThread);
+        internetFactory.setScoreFilter(40);
+
+        try {
+            internetFactory.register();
+            // Default internet request only. The first request is listen for cellular network,
+            // which is never sent to factories (it's a LISTEN, not requestable). The second
+            // fallback request is TRACK_DEFAULT which is also not sent to factories.
+            internetFactory.expectRequestAdds(1);
+            internetFactory.assertRequestCountEquals(1);
+
+            mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+            mCellNetworkAgent.connect(true);
+
+            // The internet factory however is outscored, and should lose its requests.
+            internetFactory.expectRequestRemove();
+            internetFactory.assertRequestCountEquals(0);
+
+            mCellNetworkAgent.disconnect();
+            // The network satisfying the default internet request has disconnected, so the
+            // internetFactory sees the default request again.
+            internetFactory.expectRequestAdds(1);
+            internetFactory.assertRequestCountEquals(1);
+        } finally {
+            internetFactory.terminate();
+            handlerThread.quitSafely();
+        }
+    }
+
+    /**
+     * Validate request counts are counted accurately on MOBILE_DATA_PREFERRED_UIDS change
+     * on set/replace.
+     */
+    @Test
+    public void testMobileDataPreferredUidsChangedCountsRequestsCorrectlyOnSet() throws Exception {
+        ConnectivitySettingsManager.setMobileDataPreferredUids(mServiceContext,
+                Set.of(PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID)));
+        testRequestCountLimits(() -> {
+            // Set initially to test the limit prior to having existing requests.
+            mService.updateMobileDataPreferredUids();
+            waitForIdle();
+
+            // re-set so as to test the limit as part of replacing existing requests.
+            mService.updateMobileDataPreferredUids();
+            waitForIdle();
+        });
     }
 }
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index a90fa68..20be5f4 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -57,6 +57,7 @@
 public class NsdServiceTest {
 
     static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD;
+    private static final long CLEANUP_DELAY_MS = 500;
 
     long mTimeoutMs = 100; // non-final so that tests can adjust the value.
 
@@ -86,20 +87,27 @@
     }
 
     @Test
-    public void testClientsCanConnectAndDisconnect() {
+    public void testNoDaemonStartedWhenClientsConnect() {
         when(mSettings.isEnabled()).thenReturn(true);
 
         NsdService service = makeService();
 
+        // Creating an NsdManager will not cause any cmds executed, which means
+        // no daemon is started.
         NsdManager client1 = connectClient(service);
-        verify(mDaemon, timeout(100).times(1)).start();
+        verify(mDaemon, never()).execute(any());
 
+        // Creating another NsdManager will not cause any cmds executed.
         NsdManager client2 = connectClient(service);
+        verify(mDaemon, never()).execute(any());
 
         client1.disconnect();
-        client2.disconnect();
+        // Still 1 client remains, daemon shouldn't be stopped.
+        verify(mDaemon, never()).maybeStop();
 
-        verify(mDaemon, timeout(mTimeoutMs).times(1)).stop();
+        client2.disconnect();
+        // All clients are disconnected, stop the daemon after CLEANUP_DELAY_MS.
+        verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
 
         client1.disconnect();
         client2.disconnect();
@@ -113,7 +121,8 @@
         NsdService service = makeService();
         NsdManager client = connectClient(service);
 
-        verify(mDaemon, timeout(100).times(1)).start();
+        verify(mDaemon, never()).maybeStart();
+        verify(mDaemon, never()).execute(any());
 
         NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
         request.setPort(2201);
@@ -121,21 +130,24 @@
         // Client registration request
         NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
         client.registerService(request, PROTOCOL, listener1);
+        verify(mDaemon, timeout(mTimeoutMs).times(1)).maybeStart();
         verifyDaemonCommand("register 2 a_name a_type 2201");
 
         // Client discovery request
         NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class);
         client.discoverServices("a_type", PROTOCOL, listener2);
+        verify(mDaemon, timeout(mTimeoutMs).times(1)).maybeStart();
         verifyDaemonCommand("discover 3 a_type");
 
         // Client resolve request
         NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class);
         client.resolveService(request, listener3);
+        verify(mDaemon, timeout(mTimeoutMs).times(1)).maybeStart();
         verifyDaemonCommand("resolve 4 a_name a_type local.");
 
-        // Client disconnects
+        // Client disconnects, stop the daemon after CLEANUP_DELAY_MS.
         client.disconnect();
-        verify(mDaemon, timeout(mTimeoutMs).times(1)).stop();
+        verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
 
         // checks that request are cleaned
         verifyDaemonCommands("stop-register 2", "stop-discover 3", "stop-resolve 4");
@@ -143,12 +155,38 @@
         client.disconnect();
     }
 
+    @Test
+    public void testCleanupDelayNoRequestActive() {
+        when(mSettings.isEnabled()).thenReturn(true);
+        when(mDaemon.execute(any())).thenReturn(true);
+
+        NsdService service = makeService();
+        NsdManager client = connectClient(service);
+
+        NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
+        request.setPort(2201);
+        NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
+        client.registerService(request, PROTOCOL, listener1);
+        verify(mDaemon, timeout(mTimeoutMs).times(1)).maybeStart();
+        verifyDaemonCommand("register 2 a_name a_type 2201");
+
+        client.unregisterService(listener1);
+        verifyDaemonCommand("stop-register 2");
+
+        verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
+        reset(mDaemon);
+        client.disconnect();
+        // Client disconnects, after CLEANUP_DELAY_MS, maybeStop the daemon.
+        verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
+    }
+
     NsdService makeService() {
         DaemonConnectionSupplier supplier = (callback) -> {
             mDaemonCallback = callback;
             return mDaemon;
         };
-        NsdService service = new NsdService(mContext, mSettings, mHandler, supplier);
+        NsdService service = new NsdService(mContext, mSettings,
+                mHandler, supplier, CLEANUP_DELAY_MS);
         verify(mDaemon, never()).execute(any(String.class));
         return service;
     }
@@ -157,6 +195,13 @@
         return new NsdManager(mContext, service);
     }
 
+    void verifyDelayMaybeStopDaemon(long cleanupDelayMs) {
+        // Stop daemon shouldn't be called immediately.
+        verify(mDaemon, timeout(mTimeoutMs).times(0)).maybeStop();
+        // Clean up the daemon after CLEANUP_DELAY_MS.
+        verify(mDaemon, timeout(cleanupDelayMs + mTimeoutMs)).maybeStop();
+    }
+
     void verifyDaemonCommands(String... wants) {
         verifyDaemonCommand(String.join(" ", wants), wants.length);
     }