Merge "Ignore testFactoryReset in instant app mode"
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index 5a08b23..d3b9393 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -156,7 +156,5 @@
 
 sdk {
     name: "tethering-module-sdk",
-    java_sdk_libs: [
-        "framework-tethering",
-    ],
+    bootclasspath_fragments: ["com.android.tethering-bootclasspath-fragment"],
 }
diff --git a/Tethering/bpf_progs/Android.bp b/Tethering/bpf_progs/Android.bp
index 289d75d..5b00dfe 100644
--- a/Tethering/bpf_progs/Android.bp
+++ b/Tethering/bpf_progs/Android.bp
@@ -48,10 +48,6 @@
         "-Wall",
         "-Werror",
     ],
-    include_dirs: [
-        // TODO: get rid of system/netd.
-        "system/netd/bpf_progs",             // for bpf_net_helpers.h
-    ],
 }
 
 bpf {
@@ -61,8 +57,4 @@
         "-Wall",
         "-Werror",
     ],
-    include_dirs: [
-        // TODO: get rid of system/netd.
-        "system/netd/bpf_progs",             // for bpf_net_helpers.h
-    ],
 }
diff --git a/Tethering/bpf_progs/bpf_net_helpers.h b/Tethering/bpf_progs/bpf_net_helpers.h
new file mode 100644
index 0000000..c798580
--- /dev/null
+++ b/Tethering/bpf_progs/bpf_net_helpers.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <linux/bpf.h>
+#include <linux/if_packet.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+// this returns 0 iff skb->sk is NULL
+static uint64_t (*bpf_get_socket_cookie)(struct __sk_buff* skb) = (void*)BPF_FUNC_get_socket_cookie;
+
+static uint32_t (*bpf_get_socket_uid)(struct __sk_buff* skb) = (void*)BPF_FUNC_get_socket_uid;
+
+static int (*bpf_skb_pull_data)(struct __sk_buff* skb, __u32 len) = (void*)BPF_FUNC_skb_pull_data;
+
+static int (*bpf_skb_load_bytes)(struct __sk_buff* skb, int off, void* to,
+                                 int len) = (void*)BPF_FUNC_skb_load_bytes;
+
+static int (*bpf_skb_store_bytes)(struct __sk_buff* skb, __u32 offset, const void* from, __u32 len,
+                                  __u64 flags) = (void*)BPF_FUNC_skb_store_bytes;
+
+static int64_t (*bpf_csum_diff)(__be32* from, __u32 from_size, __be32* to, __u32 to_size,
+                                __wsum seed) = (void*)BPF_FUNC_csum_diff;
+
+static int64_t (*bpf_csum_update)(struct __sk_buff* skb, __wsum csum) = (void*)BPF_FUNC_csum_update;
+
+static int (*bpf_skb_change_proto)(struct __sk_buff* skb, __be16 proto,
+                                   __u64 flags) = (void*)BPF_FUNC_skb_change_proto;
+static int (*bpf_l3_csum_replace)(struct __sk_buff* skb, __u32 offset, __u64 from, __u64 to,
+                                  __u64 flags) = (void*)BPF_FUNC_l3_csum_replace;
+static int (*bpf_l4_csum_replace)(struct __sk_buff* skb, __u32 offset, __u64 from, __u64 to,
+                                  __u64 flags) = (void*)BPF_FUNC_l4_csum_replace;
+static int (*bpf_redirect)(__u32 ifindex, __u64 flags) = (void*)BPF_FUNC_redirect;
+static int (*bpf_redirect_map)(const struct bpf_map_def* map, __u32 key,
+                               __u64 flags) = (void*)BPF_FUNC_redirect_map;
+
+static int (*bpf_skb_change_head)(struct __sk_buff* skb, __u32 head_room,
+                                  __u64 flags) = (void*)BPF_FUNC_skb_change_head;
+static int (*bpf_skb_adjust_room)(struct __sk_buff* skb, __s32 len_diff, __u32 mode,
+                                  __u64 flags) = (void*)BPF_FUNC_skb_adjust_room;
+
+// Android only supports little endian architectures
+#define htons(x) (__builtin_constant_p(x) ? ___constant_swab16(x) : __builtin_bswap16(x))
+#define htonl(x) (__builtin_constant_p(x) ? ___constant_swab32(x) : __builtin_bswap32(x))
+#define ntohs(x) htons(x)
+#define ntohl(x) htonl(x)
+
+static inline __always_inline __unused bool is_received_skb(struct __sk_buff* skb) {
+    return skb->pkt_type == PACKET_HOST || skb->pkt_type == PACKET_BROADCAST ||
+           skb->pkt_type == PACKET_MULTICAST;
+}
+
+// try to make the first 'len' header bytes readable via direct packet access
+static inline __always_inline void try_make_readable(struct __sk_buff* skb, int len) {
+    if (len > skb->len) len = skb->len;
+    if (skb->data_end - skb->data < len) bpf_skb_pull_data(skb, len);
+}
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..3a07db3 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));
@@ -1565,6 +1569,28 @@
     }
 
     /**
+     * Compare if the given NetworkCapabilities have the same UIDs.
+     *
+     * @hide
+     */
+    public static boolean hasSameUids(@Nullable NetworkCapabilities nc1,
+            @Nullable NetworkCapabilities nc2) {
+        final Set<UidRange> uids1 = (nc1 == null) ? null : nc1.mUids;
+        final Set<UidRange> uids2 = (nc2 == null) ? null : nc2.mUids;
+        if (null == uids1) return null == uids2;
+        if (null == uids2) return false;
+        // Make a copy so it can be mutated to check that all ranges in uids2 also are in uids.
+        final Set<UidRange> uids = new ArraySet<>(uids2);
+        for (UidRange range : uids1) {
+            if (!uids.contains(range)) {
+                return false;
+            }
+            uids.remove(range);
+        }
+        return uids.isEmpty();
+    }
+
+    /**
      * Tests if the set of UIDs that this network applies to is the same as the passed network.
      * <p>
      * This test only checks whether equal range objects are in both sets. It will
@@ -1580,19 +1606,7 @@
      */
     @VisibleForTesting
     public boolean equalsUids(@NonNull NetworkCapabilities nc) {
-        Set<UidRange> comparedUids = nc.mUids;
-        if (null == comparedUids) return null == mUids;
-        if (null == mUids) return false;
-        // Make a copy so it can be mutated to check that all ranges in mUids
-        // also are in uids.
-        final Set<UidRange> uids = new ArraySet<>(mUids);
-        for (UidRange range : comparedUids) {
-            if (!uids.contains(range)) {
-                return false;
-            }
-            uids.remove(range);
-        }
-        return uids.isEmpty();
+        return hasSameUids(nc, this);
     }
 
     /**
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 15b666a..9d318cd 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -79,6 +79,8 @@
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST;
+import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST;
+import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST_ONLY;
 import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired;
 import static android.os.Process.INVALID_UID;
 import static android.os.Process.VPN_UID;
@@ -398,6 +400,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 +1544,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(
@@ -2596,6 +2624,12 @@
                 "ConnectivityService");
     }
 
+    private void enforceManageTestNetworksPermission() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_TEST_NETWORKS,
+                "ConnectivityService");
+    }
+
     private boolean checkNetworkStackPermission() {
         return checkAnyPermissionOf(
                 android.Manifest.permission.NETWORK_STACK,
@@ -4146,8 +4180,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 +5628,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 +5664,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 +5688,7 @@
              */
             mCallbackFlags = NetworkCallback.FLAG_NONE;
             mCallingAttributionTag = callingAttributionTag;
+            mDefaultNetworkPriority = defaultNetworkPriority;
         }
 
         NetworkRequestInfo(int asUid, @NonNull final NetworkRequest r, @Nullable final Messenger m,
@@ -5673,6 +5718,7 @@
             mPerUidCounter.incrementCountOrThrow(mUid);
             mCallbackFlags = callbackFlags;
             mCallingAttributionTag = callingAttributionTag;
+            mDefaultNetworkPriority = DEFAULT_NETWORK_PRIORITY_NONE;
             linkDeathRecipient();
         }
 
@@ -5712,15 +5758,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
@@ -7300,6 +7349,8 @@
             mDnsManager.updateTransportsForNetwork(
                     nai.network.getNetId(), newNc.getTransportTypes());
         }
+
+        maybeSendProxyBroadcast(nai, prevNc, newNc);
     }
 
     /** Convenience method to update the capabilities for a given network. */
@@ -7377,9 +7428,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 +
@@ -7388,6 +7443,30 @@
         maybeCloseSockets(nai, ranges, exemptUids);
     }
 
+    private boolean isProxySetOnAnyDefaultNetwork() {
+        ensureRunningOnConnectivityServiceThread();
+        for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
+            final NetworkAgentInfo nai = nri.getSatisfier();
+            if (nai != null && nai.linkProperties.getHttpProxy() != null) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void maybeSendProxyBroadcast(NetworkAgentInfo nai, NetworkCapabilities prevNc,
+            NetworkCapabilities newNc) {
+        // When the apps moved from/to a VPN, a proxy broadcast is needed to inform the apps that
+        // the proxy might be changed since the default network satisfied by the apps might also
+        // changed.
+        // TODO: Try to track the default network that apps use and only send a proxy broadcast when
+        //  that happens to prevent false alarms.
+        if (nai.isVPN() && nai.everConnected && !NetworkCapabilities.hasSameUids(prevNc, newNc)
+                && (nai.linkProperties.getHttpProxy() != null || isProxySetOnAnyDefaultNetwork())) {
+            mProxyTracker.sendProxyBroadcast();
+        }
+    }
+
     private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc,
             NetworkCapabilities newNc) {
         Set<UidRange> prevRanges = null == prevNc ? null : prevNc.getUidRanges();
@@ -7690,14 +7769,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 +9838,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 +9909,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;
     }
 
@@ -9876,8 +9961,15 @@
             @NonNull final OemNetworkPreferences preference,
             @Nullable final IOnCompleteListener listener) {
 
-        enforceAutomotiveDevice();
-        enforceOemNetworkPreferencesPermission();
+        Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
+        // Only bypass the permission/device checks if this is a valid test request.
+        if (isValidTestOemNetworkPreference(preference)) {
+            enforceManageTestNetworksPermission();
+        } else {
+            enforceAutomotiveDevice();
+            enforceOemNetworkPreferencesPermission();
+            validateOemNetworkPreferences(preference);
+        }
 
         // TODO: Have a priority for each preference.
         if (!mProfileNetworkPreferences.isEmpty() || !mMobileDataPreferredUids.isEmpty()) {
@@ -9889,18 +9981,41 @@
             throwConcurrentPreferenceException();
         }
 
-        Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
-        validateOemNetworkPreferences(preference);
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_OEM_NETWORK_PREFERENCE,
                 new Pair<>(preference, listener)));
     }
 
+    /**
+     * Check the validity of an OEM network preference to be used for testing purposes.
+     * @param preference the preference to validate
+     * @return true if this is a valid OEM network preference test request.
+     */
+    private boolean isValidTestOemNetworkPreference(
+            @NonNull final OemNetworkPreferences preference) {
+        // Allow for clearing of an existing OemNetworkPreference used for testing.
+        // This isn't called on the handler thread so it is possible that mOemNetworkPreferences
+        // changes after this check is complete. This is an unlikely scenario as calling of this API
+        // is controlled by the OEM therefore the added complexity is not worth adding given those
+        // circumstances. That said, it is an edge case to be aware of hence this comment.
+        final boolean isValidTestClearPref = preference.getNetworkPreferences().size() == 0
+                && isTestOemNetworkPreference(mOemNetworkPreferences);
+        return isTestOemNetworkPreference(preference) || isValidTestClearPref;
+    }
+
+    private boolean isTestOemNetworkPreference(@NonNull final OemNetworkPreferences preference) {
+        final Map<String, Integer> prefMap = preference.getNetworkPreferences();
+        return prefMap.size() == 1
+                && (prefMap.containsValue(OEM_NETWORK_PREFERENCE_TEST)
+                || prefMap.containsValue(OEM_NETWORK_PREFERENCE_TEST_ONLY));
+    }
+
     private void validateOemNetworkPreferences(@NonNull OemNetworkPreferences preference) {
         for (@OemNetworkPreferences.OemNetworkPreference final int pref
                 : preference.getNetworkPreferences().values()) {
-            if (OemNetworkPreferences.OEM_NETWORK_PREFERENCE_UNINITIALIZED == pref) {
-                final String msg = "OEM_NETWORK_PREFERENCE_UNINITIALIZED is an invalid value.";
-                throw new IllegalArgumentException(msg);
+            if (pref <= 0 || OemNetworkPreferences.OEM_NETWORK_PREFERENCE_MAX < pref) {
+                throw new IllegalArgumentException(
+                        OemNetworkPreferences.oemNetworkPreferenceToString(pref)
+                                + " is an invalid value.");
             }
         }
     }
@@ -10069,7 +10184,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 +10192,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(
@@ -10124,18 +10239,27 @@
                 case OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY:
                     requests.add(createOemPrivateNetworkRequest());
                     break;
+                case OEM_NETWORK_PREFERENCE_TEST:
+                    requests.add(createUnmeteredNetworkRequest());
+                    requests.add(createTestNetworkRequest());
+                    requests.add(createDefaultRequest());
+                    break;
+                case OEM_NETWORK_PREFERENCE_TEST_ONLY:
+                    requests.add(createTestNetworkRequest());
+                    break;
                 default:
                     // This should never happen.
                     throw new IllegalArgumentException("createNriFromOemNetworkPreferences()"
                             + " called with invalid preference of " + preference);
             }
 
-            final ArraySet ranges = new ArraySet<Integer>();
+            final ArraySet<UidRange> ranges = new ArraySet<>();
             for (final int uid : uids) {
                 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() {
@@ -10162,10 +10286,17 @@
         }
 
         private NetworkCapabilities createDefaultPerAppNetCap() {
-            final NetworkCapabilities netCap = new NetworkCapabilities();
-            netCap.addCapability(NET_CAPABILITY_INTERNET);
-            netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName());
-            return netCap;
+            final NetworkCapabilities netcap = new NetworkCapabilities();
+            netcap.addCapability(NET_CAPABILITY_INTERNET);
+            netcap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName());
+            return netcap;
+        }
+
+        private NetworkRequest createTestNetworkRequest() {
+            final NetworkCapabilities netcap = new NetworkCapabilities();
+            netcap.clearAll();
+            netcap.addTransportType(TRANSPORT_TEST);
+            return createNetworkRequest(NetworkRequest.Type.REQUEST, netcap);
         }
     }
 }
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/Android.bp b/tests/cts/net/Android.bp
index cd69b13..e28c33f 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -38,6 +38,7 @@
     srcs: [
         "src/**/*.java",
         "src/**/*.kt",
+        ":ike-aes-xcbc",
     ],
     jarjar_rules: "jarjar-rules-shared.txt",
     static_libs: [
diff --git a/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
index a889c41..f993aed 100644
--- a/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
+++ b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
@@ -20,6 +20,7 @@
 import android.Manifest.permission.NETWORK_SETTINGS
 import android.Manifest.permission.READ_DEVICE_CONFIG
 import android.content.pm.PackageManager.FEATURE_TELEPHONY
+import android.content.pm.PackageManager.FEATURE_WATCH
 import android.content.pm.PackageManager.FEATURE_WIFI
 import android.net.ConnectivityManager
 import android.net.ConnectivityManager.NetworkCallback
@@ -57,6 +58,7 @@
 import junit.framework.AssertionFailedError
 import org.junit.After
 import org.junit.Assume.assumeTrue
+import org.junit.Assume.assumeFalse
 import org.junit.Before
 import org.junit.runner.RunWith
 import java.util.concurrent.CompletableFuture
@@ -128,6 +130,7 @@
     fun testCaptivePortalIsNotDefaultNetwork() {
         assumeTrue(pm.hasSystemFeature(FEATURE_TELEPHONY))
         assumeTrue(pm.hasSystemFeature(FEATURE_WIFI))
+        assumeFalse(pm.hasSystemFeature(FEATURE_WATCH))
         utils.ensureWifiConnected()
         val cellNetwork = utils.connectToCell()
 
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 8f52b1d..8b8a8fb 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -65,11 +65,13 @@
 import static android.system.OsConstants.AF_INET6;
 import static android.system.OsConstants.AF_UNSPEC;
 
+import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
 import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_LOCKDOWN_VPN;
 import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_NONE;
 import static com.android.testutils.MiscAsserts.assertThrows;
+import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
 import static com.android.testutils.TestPermissionUtil.runAsShell;
 
 import static org.junit.Assert.assertEquals;
@@ -112,6 +114,7 @@
 import android.net.NetworkSpecifier;
 import android.net.NetworkStateSnapshot;
 import android.net.NetworkUtils;
+import android.net.OemNetworkPreferences;
 import android.net.ProxyInfo;
 import android.net.SocketKeepalive;
 import android.net.TelephonyNetworkSpecifier;
@@ -151,9 +154,11 @@
 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;
+import com.android.testutils.TestNetworkTracker;
 import com.android.testutils.TestableNetworkCallback;
 
 import junit.framework.AssertionFailedError;
@@ -190,6 +195,7 @@
 import java.util.concurrent.Executor;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.function.Supplier;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -460,6 +466,7 @@
                 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
     }
 
+    @AppModeFull(reason = "Cannot request network in instant app mode")
     @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
     @Test
     public void testGetAllNetworkStateSnapshots()
@@ -516,13 +523,8 @@
     @Test
     @SkipPresubmit(reason = "Virtual devices use a single internet connection for all networks")
     public void testOpenConnection() throws Exception {
-        boolean canRunTest = mPackageManager.hasSystemFeature(FEATURE_WIFI)
-                && mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
-        if (!canRunTest) {
-            Log.i(TAG,"testOpenConnection cannot execute unless device supports both WiFi "
-                    + "and a cellular connection");
-            return;
-        }
+        assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
+        assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY));
 
         Network wifiNetwork = mCtsNetUtils.connectToWifi();
         Network cellNetwork = mCtsNetUtils.connectToCell();
@@ -660,6 +662,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.
@@ -672,10 +699,7 @@
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
     @Test
     public void testRegisterNetworkCallback() {
-        if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
-            Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi");
-            return;
-        }
+        assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
 
         // We will register for a WIFI network being available or lost.
         final TestNetworkCallback callback = new TestNetworkCallback();
@@ -739,10 +763,7 @@
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
     @Test
     public void testRegisterNetworkCallback_withPendingIntent() {
-        if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
-            Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi");
-            return;
-        }
+        assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
 
         // Create a ConnectivityActionReceiver that has an IntentFilter for our locally defined
         // action, NETWORK_CALLBACK_ACTION.
@@ -913,7 +934,8 @@
         }
     }
 
-    private void waitForActiveNetworkMetered(int targetTransportType, boolean requestedMeteredness)
+    private void waitForActiveNetworkMetered(final int targetTransportType,
+            final boolean requestedMeteredness, final boolean useSystemDefault)
             throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
         final NetworkCallback networkCallback = new NetworkCallback() {
@@ -927,17 +949,36 @@
                 }
             }
         };
-        // Registering a callback here guarantees onCapabilitiesChanged is called immediately
-        // with the current setting. Therefore, if the setting has already been changed,
-        // this method will return right away, and if not it will wait for the setting to change.
-        mCm.registerDefaultNetworkCallback(networkCallback);
-        // Changing meteredness on wifi involves reconnecting, which can take several seconds
-        // (involves re-associating, DHCP...).
-        if (!latch.await(NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
-            fail("Timed out waiting for active network metered status to change to "
-                 + requestedMeteredness + " ; network = " + mCm.getActiveNetwork());
+
+        try {
+            // Registering a callback here guarantees onCapabilitiesChanged is called immediately
+            // with the current setting. Therefore, if the setting has already been changed,
+            // this method will return right away, and if not, it'll wait for the setting to change.
+            if (useSystemDefault) {
+                runWithShellPermissionIdentity(() ->
+                                mCmShim.registerSystemDefaultNetworkCallback(networkCallback,
+                                        new Handler(Looper.getMainLooper())),
+                        NETWORK_SETTINGS);
+            } else {
+                mCm.registerDefaultNetworkCallback(networkCallback);
+            }
+
+            // Changing meteredness on wifi involves reconnecting, which can take several seconds
+            // (involves re-associating, DHCP...).
+            if (!latch.await(NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                fail("Timed out waiting for active network metered status to change to "
+                        + requestedMeteredness + " ; network = " + mCm.getActiveNetwork());
+            }
+        } finally {
+            mCm.unregisterNetworkCallback(networkCallback);
         }
-        mCm.unregisterNetworkCallback(networkCallback);
+    }
+
+    private void setWifiMeteredStatusAndWait(String ssid, boolean isMetered) throws Exception {
+        setWifiMeteredStatus(ssid, Boolean.toString(isMetered) /* metered */);
+        waitForActiveNetworkMetered(TRANSPORT_WIFI,
+                isMetered /* requestedMeteredness */,
+                true /* useSystemDefault */);
     }
 
     private void assertMultipathPreferenceIsEventually(Network network, int oldValue,
@@ -999,10 +1040,9 @@
             int newMeteredPreference = findNextPrefValue(resolver);
             Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE,
                     Integer.toString(newMeteredPreference));
-            setWifiMeteredStatus(ssid, "true");
-            waitForActiveNetworkMetered(TRANSPORT_WIFI, true);
             // Wifi meterness changes from unmetered to metered will disconnect and reconnect since
             // R.
+            setWifiMeteredStatusAndWait(ssid, true);
             final Network network = mCtsNetUtils.ensureWifiConnected();
             assertEquals(ssid, unquoteSSID(mWifiManager.getConnectionInfo().getSSID()));
             assertEquals(mCm.getNetworkCapabilities(network).hasCapability(
@@ -1019,9 +1059,8 @@
             assertMultipathPreferenceIsEventually(network,
                     oldMeteredPreference, newMeteredPreference);
 
-            setWifiMeteredStatus(ssid, "false");
             // No disconnect from unmetered to metered.
-            waitForActiveNetworkMetered(TRANSPORT_WIFI, false);
+            setWifiMeteredStatusAndWait(ssid, false);
             assertEquals(mCm.getNetworkCapabilities(network).hasCapability(
                     NET_CAPABILITY_NOT_METERED), true);
             assertMultipathPreferenceIsEventually(network, newMeteredPreference,
@@ -1211,11 +1250,7 @@
     @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
     @Test
     public void testKeepaliveWifiUnsupported() throws Exception {
-        if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
-            Log.i(TAG, "testKeepaliveUnsupported cannot execute unless device"
-                    + " supports WiFi");
-            return;
-        }
+        assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
 
         final Network network = mCtsNetUtils.ensureWifiConnected();
         if (getSupportedKeepalivesForNet(network) != 0) return;
@@ -1232,10 +1267,7 @@
     @Test
     @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
     public void testCreateTcpKeepalive() throws Exception {
-        if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
-            Log.i(TAG, "testCreateTcpKeepalive cannot execute unless device supports WiFi");
-            return;
-        }
+        assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
 
         final Network network = mCtsNetUtils.ensureWifiConnected();
         if (getSupportedKeepalivesForNet(network) == 0) return;
@@ -1442,11 +1474,7 @@
     @Test
     @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
     public void testSocketKeepaliveLimitWifi() throws Exception {
-        if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
-            Log.i(TAG, "testSocketKeepaliveLimitWifi cannot execute unless device"
-                    + " supports WiFi");
-            return;
-        }
+        assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
 
         final Network network = mCtsNetUtils.ensureWifiConnected();
         final int supported = getSupportedKeepalivesForNet(network);
@@ -1539,11 +1567,7 @@
     @Test
     @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
     public void testSocketKeepaliveUnprivileged() throws Exception {
-        if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
-            Log.i(TAG, "testSocketKeepaliveUnprivileged cannot execute unless device"
-                    + " supports WiFi");
-            return;
-        }
+        assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
 
         final Network network = mCtsNetUtils.ensureWifiConnected();
         final int supported = getSupportedKeepalivesForNet(network);
@@ -1690,6 +1714,24 @@
                 c -> c instanceof CallbackEntry.Available);
     }
 
+    private void waitForAvailable(
+            @NonNull final TestableNetworkCallback cb, final int expectedTransport) {
+        cb.eventuallyExpect(
+                CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
+                entry -> {
+                    final NetworkCapabilities nc = mCm.getNetworkCapabilities(entry.getNetwork());
+                    return nc.hasTransport(expectedTransport);
+                }
+        );
+    }
+
+    private void waitForAvailable(
+            @NonNull final TestableNetworkCallback cb, @NonNull final Network expectedNetwork) {
+        cb.expectAvailableCallbacks(expectedNetwork, false /* suspended */,
+                true /* validated */,
+                false /* blocked */, NETWORK_CALLBACK_TIMEOUT_MS);
+    }
+
     private void waitForLost(@NonNull final TestableNetworkCallback cb) {
         cb.eventuallyExpect(CallbackEntry.LOST, NETWORK_CALLBACK_TIMEOUT_MS,
                 c -> c instanceof CallbackEntry.Lost);
@@ -2076,4 +2118,172 @@
         startTetheringCallback.verifyTetheringStarted();
         callback.expectTetheredInterfacesChanged(wifiRegexs, TETHERING_WIFI);
     }
+
+    /**
+     * Verify that per-app OEM network preference functions as expected for network preference TEST.
+     * For specified apps, validate networks are prioritized in order: unmetered, TEST transport,
+     * default network.
+     */
+    @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+    @Test
+    public void testSetOemNetworkPreferenceForTestPref() throws Exception {
+        // Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
+        // shims, and @IgnoreUpTo does not check that.
+        assumeTrue(TestUtils.shouldTestSApis());
+        assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
+
+        final TestNetworkTracker tnt = callWithShellPermissionIdentity(
+                () -> initTestNetwork(mContext, TEST_LINKADDR, NETWORK_CALLBACK_TIMEOUT_MS));
+        final TestableNetworkCallback defaultCallback = new TestableNetworkCallback();
+        final TestableNetworkCallback systemDefaultCallback = new TestableNetworkCallback();
+
+        final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
+        final NetworkCapabilities wifiNetworkCapabilities = callWithShellPermissionIdentity(
+                () -> mCm.getNetworkCapabilities(wifiNetwork));
+        final String ssid = unquoteSSID(wifiNetworkCapabilities.getSsid());
+        final boolean oldMeteredValue = wifiNetworkCapabilities.isMetered();
+
+        try {
+            // This network will be used for unmetered.
+            setWifiMeteredStatusAndWait(ssid, false /* isMetered */);
+
+            setOemNetworkPreferenceForMyPackage(OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST);
+            registerTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
+
+            // Validate that an unmetered network is used over other networks.
+            waitForAvailable(defaultCallback, wifiNetwork);
+            waitForAvailable(systemDefaultCallback, wifiNetwork);
+
+            // Validate when setting unmetered to metered, unmetered is lost and replaced by the
+            // network with the TEST transport.
+            setWifiMeteredStatusAndWait(ssid, true /* isMetered */);
+            defaultCallback.expectCallback(CallbackEntry.LOST, wifiNetwork);
+            waitForAvailable(defaultCallback, tnt.getNetwork());
+            // Depending on if this device has cellular connectivity or not, multiple available
+            // callbacks may be received. Eventually, metered Wi-Fi should be the final available
+            // callback in any case therefore confirm its receipt before continuing to assure the
+            // system is in the expected state.
+            waitForAvailable(systemDefaultCallback, TRANSPORT_WIFI);
+        } finally {
+            // Validate that removing the test network will fallback to the default network.
+            runWithShellPermissionIdentity(tnt::teardown);
+            defaultCallback.expectCallback(CallbackEntry.LOST, tnt.getNetwork());
+            waitForAvailable(defaultCallback);
+
+            setWifiMeteredStatusAndWait(ssid, oldMeteredValue);
+
+            // Cleanup any prior test state from setOemNetworkPreference
+            clearOemNetworkPreference();
+            unregisterTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
+        }
+    }
+
+    /**
+     * Verify that per-app OEM network preference functions as expected for network pref TEST_ONLY.
+     * For specified apps, validate that only TEST transport type networks are used.
+     */
+    @Test
+    public void testSetOemNetworkPreferenceForTestOnlyPref() throws Exception {
+        // Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
+        // shims, and @IgnoreUpTo does not check that.
+        assumeTrue(TestUtils.shouldTestSApis());
+
+        final TestNetworkTracker tnt = callWithShellPermissionIdentity(
+                () -> initTestNetwork(mContext, TEST_LINKADDR, NETWORK_CALLBACK_TIMEOUT_MS));
+        final TestableNetworkCallback defaultCallback = new TestableNetworkCallback();
+        final TestableNetworkCallback systemDefaultCallback = new TestableNetworkCallback();
+
+        final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
+
+        try {
+            setOemNetworkPreferenceForMyPackage(
+                    OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST_ONLY);
+            registerTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
+            waitForAvailable(defaultCallback, tnt.getNetwork());
+            waitForAvailable(systemDefaultCallback, wifiNetwork);
+        } finally {
+            runWithShellPermissionIdentity(tnt::teardown);
+            defaultCallback.expectCallback(CallbackEntry.LOST, tnt.getNetwork());
+
+            // This network preference should only ever use the test network therefore available
+            // should not trigger when the test network goes down (e.g. switch to cellular).
+            defaultCallback.assertNoCallback();
+            // The system default should still be connected to Wi-fi
+            assertEquals(wifiNetwork, systemDefaultCallback.getLastAvailableNetwork());
+
+            // Cleanup any prior test state from setOemNetworkPreference
+            clearOemNetworkPreference();
+
+            // The default (non-test) network should be available as the network pref was cleared.
+            waitForAvailable(defaultCallback);
+            unregisterTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
+        }
+    }
+
+    private void unregisterTestOemNetworkPreferenceCallbacks(
+            @NonNull final TestableNetworkCallback defaultCallback,
+            @NonNull final TestableNetworkCallback systemDefaultCallback) {
+        mCm.unregisterNetworkCallback(defaultCallback);
+        mCm.unregisterNetworkCallback(systemDefaultCallback);
+    }
+
+    private void registerTestOemNetworkPreferenceCallbacks(
+            @NonNull final TestableNetworkCallback defaultCallback,
+            @NonNull final TestableNetworkCallback systemDefaultCallback) {
+        mCm.registerDefaultNetworkCallback(defaultCallback);
+        runWithShellPermissionIdentity(() ->
+                mCmShim.registerSystemDefaultNetworkCallback(systemDefaultCallback,
+                        new Handler(Looper.getMainLooper())), NETWORK_SETTINGS);
+    }
+
+    private static final class OnCompleteListenerCallback {
+        final CompletableFuture<Object> mDone = new CompletableFuture<>();
+
+        public void onComplete() {
+            mDone.complete(new Object());
+        }
+
+        void expectOnComplete() throws Exception {
+            try {
+                mDone.get(NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            } catch (TimeoutException e) {
+                fail("Expected onComplete() not received after "
+                        + NETWORK_CALLBACK_TIMEOUT_MS + " ms");
+            }
+        }
+    }
+
+    private void setOemNetworkPreferenceForMyPackage(final int networkPref) throws Exception {
+        final OemNetworkPreferences pref = new OemNetworkPreferences.Builder()
+                .addNetworkPreference(mContext.getPackageName(), networkPref)
+                .build();
+        final OnCompleteListenerCallback oemPrefListener = new OnCompleteListenerCallback();
+        mUiAutomation.adoptShellPermissionIdentity();
+        try {
+            mCm.setOemNetworkPreference(
+                    pref, mContext.getMainExecutor(), oemPrefListener::onComplete);
+        } finally {
+            mUiAutomation.dropShellPermissionIdentity();
+        }
+        oemPrefListener.expectOnComplete();
+    }
+
+    /**
+     * This will clear the OEM network preference on the device. As there is currently no way of
+     * getting the existing preference, if this is executed while an existing preference is in
+     * place, that preference will need to be reapplied after executing this test.
+     * @throws Exception
+     */
+    private void clearOemNetworkPreference() throws Exception {
+        final OemNetworkPreferences clearPref = new OemNetworkPreferences.Builder().build();
+        final OnCompleteListenerCallback oemPrefListener = new OnCompleteListenerCallback();
+        mUiAutomation.adoptShellPermissionIdentity();
+        try {
+            mCm.setOemNetworkPreference(
+                    clearPref, mContext.getMainExecutor(), oemPrefListener::onComplete);
+        } finally {
+            mUiAutomation.dropShellPermissionIdentity();
+        }
+        oemPrefListener.expectOnComplete();
+    }
 }
diff --git a/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java b/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java
index c6d8d65..6e9f0cd 100644
--- a/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java
+++ b/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java
@@ -39,13 +39,11 @@
 import android.net.ConnectivityManager;
 import android.net.Ikev2VpnProfile;
 import android.net.IpSecAlgorithm;
-import android.net.LinkAddress;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
 import android.net.ProxyInfo;
 import android.net.TestNetworkInterface;
-import android.net.TestNetworkManager;
 import android.net.VpnManager;
 import android.net.cts.util.CtsNetUtils;
 import android.os.Build;
@@ -439,41 +437,37 @@
         assertEquals(vpnNetwork, cb.lastLostNetwork);
     }
 
-    private void doTestStartStopVpnProfile(boolean testIpv6) throws Exception {
-        // Non-final; these variables ensure we clean up properly after our test if we have
-        // allocated test network resources
-        final TestNetworkManager tnm = sContext.getSystemService(TestNetworkManager.class);
-        TestNetworkInterface testIface = null;
-        TestNetworkCallback tunNetworkCallback = null;
+    private class VerifyStartStopVpnProfileTest implements TestNetworkRunnable.Test {
+        private final boolean mTestIpv6Only;
 
-        try {
-            // Build underlying test network
-            testIface = tnm.createTunInterface(
-                    new LinkAddress[] {
-                            new LinkAddress(LOCAL_OUTER_4, IP4_PREFIX_LEN),
-                            new LinkAddress(LOCAL_OUTER_6, IP6_PREFIX_LEN)});
+        /**
+         * Constructs the test
+         *
+         * @param testIpv6Only if true, builds a IPv6-only test; otherwise builds a IPv4-only test
+         */
+        VerifyStartStopVpnProfileTest(boolean testIpv6Only) {
+            mTestIpv6Only = testIpv6Only;
+        }
 
-            // Hold on to this callback to ensure network does not get reaped.
-            tunNetworkCallback = mCtsNetUtils.setupAndGetTestNetwork(
-                    testIface.getInterfaceName());
+        @Override
+        public void runTest(TestNetworkInterface testIface, TestNetworkCallback tunNetworkCallback)
+                throws Exception {
             final IkeTunUtils tunUtils = new IkeTunUtils(testIface.getFileDescriptor());
 
-            checkStartStopVpnProfileBuildsNetworks(tunUtils, testIpv6);
-        } finally {
-            // Make sure to stop the VPN profile. This is safe to call multiple times.
+            checkStartStopVpnProfileBuildsNetworks(tunUtils, mTestIpv6Only);
+        }
+
+        @Override
+        public void cleanupTest() {
             sVpnMgr.stopProvisionedVpnProfile();
+        }
 
-            if (testIface != null) {
-                testIface.getFileDescriptor().close();
-            }
-
-            if (tunNetworkCallback != null) {
-                sCM.unregisterNetworkCallback(tunNetworkCallback);
-            }
-
-            final Network testNetwork = tunNetworkCallback.currentNetwork;
-            if (testNetwork != null) {
-                tnm.teardownTestNetwork(testNetwork);
+        @Override
+        public InetAddress[] getTestNetworkAddresses() {
+            if (mTestIpv6Only) {
+                return new InetAddress[] {LOCAL_OUTER_6};
+            } else {
+                return new InetAddress[] {LOCAL_OUTER_4};
             }
         }
     }
@@ -483,9 +477,8 @@
         assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
 
         // Requires shell permission to update appops.
-        runWithShellPermissionIdentity(() -> {
-            doTestStartStopVpnProfile(false);
-        });
+        runWithShellPermissionIdentity(
+                new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(false)));
     }
 
     @Test
@@ -493,9 +486,8 @@
         assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
 
         // Requires shell permission to update appops.
-        runWithShellPermissionIdentity(() -> {
-            doTestStartStopVpnProfile(true);
-        });
+        runWithShellPermissionIdentity(
+                new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(true)));
     }
 
     private static class CertificateAndKey {
diff --git a/tests/cts/net/src/android/net/cts/IpSecAlgorithmImplTest.java b/tests/cts/net/src/android/net/cts/IpSecAlgorithmImplTest.java
new file mode 100644
index 0000000..2f29273
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/IpSecAlgorithmImplTest.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.cts;
+
+import static android.net.IpSecAlgorithm.AUTH_AES_CMAC;
+import static android.net.IpSecAlgorithm.AUTH_AES_XCBC;
+import static android.net.IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305;
+import static android.net.IpSecAlgorithm.CRYPT_AES_CTR;
+import static android.net.cts.PacketUtils.AES_CMAC;
+import static android.net.cts.PacketUtils.AES_CMAC_ICV_LEN;
+import static android.net.cts.PacketUtils.AES_CMAC_KEY_LEN;
+import static android.net.cts.PacketUtils.AES_CTR;
+import static android.net.cts.PacketUtils.AES_CTR_BLK_SIZE;
+import static android.net.cts.PacketUtils.AES_CTR_IV_LEN;
+import static android.net.cts.PacketUtils.AES_CTR_KEY_LEN_20;
+import static android.net.cts.PacketUtils.AES_CTR_KEY_LEN_28;
+import static android.net.cts.PacketUtils.AES_CTR_KEY_LEN_36;
+import static android.net.cts.PacketUtils.AES_CTR_SALT_LEN;
+import static android.net.cts.PacketUtils.AES_XCBC;
+import static android.net.cts.PacketUtils.AES_XCBC_ICV_LEN;
+import static android.net.cts.PacketUtils.AES_XCBC_KEY_LEN;
+import static android.net.cts.PacketUtils.CHACHA20_POLY1305;
+import static android.net.cts.PacketUtils.CHACHA20_POLY1305_BLK_SIZE;
+import static android.net.cts.PacketUtils.CHACHA20_POLY1305_ICV_LEN;
+import static android.net.cts.PacketUtils.CHACHA20_POLY1305_IV_LEN;
+import static android.net.cts.PacketUtils.CHACHA20_POLY1305_KEY_LEN;
+import static android.net.cts.PacketUtils.CHACHA20_POLY1305_SALT_LEN;
+import static android.net.cts.PacketUtils.ESP_HDRLEN;
+import static android.net.cts.PacketUtils.IP6_HDRLEN;
+import static android.net.cts.PacketUtils.getIpHeader;
+import static android.net.cts.util.CtsNetUtils.TestNetworkCallback;
+
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assume.assumeTrue;
+
+import android.net.IpSecAlgorithm;
+import android.net.IpSecManager;
+import android.net.IpSecTransform;
+import android.net.Network;
+import android.net.TestNetworkInterface;
+import android.net.cts.PacketUtils.BytePayload;
+import android.net.cts.PacketUtils.EspAeadCipher;
+import android.net.cts.PacketUtils.EspAuth;
+import android.net.cts.PacketUtils.EspAuthNull;
+import android.net.cts.PacketUtils.EspCipher;
+import android.net.cts.PacketUtils.EspCipherNull;
+import android.net.cts.PacketUtils.EspCryptCipher;
+import android.net.cts.PacketUtils.EspHeader;
+import android.net.cts.PacketUtils.IpHeader;
+import android.net.cts.PacketUtils.UdpHeader;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "Socket cannot bind in instant app mode")
+public class IpSecAlgorithmImplTest extends IpSecBaseTest {
+    private static final InetAddress LOCAL_ADDRESS =
+            InetAddress.parseNumericAddress("2001:db8:1::1");
+    private static final InetAddress REMOTE_ADDRESS =
+            InetAddress.parseNumericAddress("2001:db8:1::2");
+
+    private static final int REMOTE_PORT = 12345;
+    private static final IpSecManager sIpSecManager =
+            InstrumentationRegistry.getContext().getSystemService(IpSecManager.class);
+
+    private static class CheckCryptoImplTest implements TestNetworkRunnable.Test {
+        private final IpSecAlgorithm mIpsecEncryptAlgo;
+        private final IpSecAlgorithm mIpsecAuthAlgo;
+        private final IpSecAlgorithm mIpsecAeadAlgo;
+        private final EspCipher mEspCipher;
+        private final EspAuth mEspAuth;
+
+        CheckCryptoImplTest(
+                IpSecAlgorithm ipsecEncryptAlgo,
+                IpSecAlgorithm ipsecAuthAlgo,
+                IpSecAlgorithm ipsecAeadAlgo,
+                EspCipher espCipher,
+                EspAuth espAuth) {
+            mIpsecEncryptAlgo = ipsecEncryptAlgo;
+            mIpsecAuthAlgo = ipsecAuthAlgo;
+            mIpsecAeadAlgo = ipsecAeadAlgo;
+            mEspCipher = espCipher;
+            mEspAuth = espAuth;
+        }
+
+        private static byte[] buildTransportModeEspPayload(
+                int srcPort, int dstPort, int spi, EspCipher espCipher, EspAuth espAuth)
+                throws Exception {
+            final UdpHeader udpPayload =
+                    new UdpHeader(srcPort, dstPort, new BytePayload(TEST_DATA));
+            final IpHeader preEspIpHeader =
+                    getIpHeader(
+                            udpPayload.getProtocolId(), LOCAL_ADDRESS, REMOTE_ADDRESS, udpPayload);
+
+            final PacketUtils.EspHeader espPayload =
+                    new EspHeader(
+                            udpPayload.getProtocolId(),
+                            spi,
+                            1 /* sequence number */,
+                            udpPayload.getPacketBytes(preEspIpHeader),
+                            espCipher,
+                            espAuth);
+            return espPayload.getPacketBytes(preEspIpHeader);
+        }
+
+        @Override
+        public void runTest(TestNetworkInterface testIface, TestNetworkCallback tunNetworkCallback)
+                throws Exception {
+            final TunUtils tunUtils = new TunUtils(testIface.getFileDescriptor());
+            tunNetworkCallback.waitForAvailable();
+            final Network testNetwork = tunNetworkCallback.currentNetwork;
+
+            final IpSecTransform.Builder transformBuilder =
+                    new IpSecTransform.Builder(InstrumentationRegistry.getContext());
+            if (mIpsecAeadAlgo != null) {
+                transformBuilder.setAuthenticatedEncryption(mIpsecAeadAlgo);
+            } else {
+                if (mIpsecEncryptAlgo != null) {
+                    transformBuilder.setEncryption(mIpsecEncryptAlgo);
+                }
+                if (mIpsecAuthAlgo != null) {
+                    transformBuilder.setAuthentication(mIpsecAuthAlgo);
+                }
+            }
+
+            try (IpSecManager.SecurityParameterIndex outSpi =
+                            sIpSecManager.allocateSecurityParameterIndex(REMOTE_ADDRESS);
+                    IpSecManager.SecurityParameterIndex inSpi =
+                            sIpSecManager.allocateSecurityParameterIndex(LOCAL_ADDRESS);
+                    IpSecTransform outTransform =
+                            transformBuilder.buildTransportModeTransform(LOCAL_ADDRESS, outSpi);
+                    IpSecTransform inTransform =
+                            transformBuilder.buildTransportModeTransform(REMOTE_ADDRESS, inSpi);
+                    // Bind localSocket to a random available port.
+                    DatagramSocket localSocket = new DatagramSocket(0)) {
+                sIpSecManager.applyTransportModeTransform(
+                        localSocket, IpSecManager.DIRECTION_IN, inTransform);
+                sIpSecManager.applyTransportModeTransform(
+                        localSocket, IpSecManager.DIRECTION_OUT, outTransform);
+
+                // Send ESP packet
+                final DatagramPacket outPacket =
+                        new DatagramPacket(
+                                TEST_DATA, 0, TEST_DATA.length, REMOTE_ADDRESS, REMOTE_PORT);
+                testNetwork.bindSocket(localSocket);
+                localSocket.send(outPacket);
+                final byte[] outEspPacket =
+                        tunUtils.awaitEspPacket(outSpi.getSpi(), false /* useEncap */);
+
+                // Remove transform for good hygiene
+                sIpSecManager.removeTransportModeTransforms(localSocket);
+
+                // Get the kernel-generated ESP payload
+                final byte[] outEspPayload = new byte[outEspPacket.length - IP6_HDRLEN];
+                System.arraycopy(outEspPacket, IP6_HDRLEN, outEspPayload, 0, outEspPayload.length);
+
+                // Get the IV of the kernel-generated ESP payload
+                final byte[] iv =
+                        Arrays.copyOfRange(
+                                outEspPayload, ESP_HDRLEN, ESP_HDRLEN + mEspCipher.ivLen);
+
+                // Build ESP payload using the kernel-generated IV and the user space crypto
+                // implementations
+                mEspCipher.updateIv(iv);
+                final byte[] expectedEspPayload =
+                        buildTransportModeEspPayload(
+                                localSocket.getLocalPort(),
+                                REMOTE_PORT,
+                                outSpi.getSpi(),
+                                mEspCipher,
+                                mEspAuth);
+
+                // Compare user-space-generated and kernel-generated ESP payload
+                assertArrayEquals(expectedEspPayload, outEspPayload);
+            }
+        }
+
+        @Override
+        public void cleanupTest() {
+            // Do nothing
+        }
+
+        @Override
+        public InetAddress[] getTestNetworkAddresses() {
+            return new InetAddress[] {LOCAL_ADDRESS};
+        }
+    }
+
+    private void checkAesCtr(int keyLen) throws Exception {
+        final byte[] cryptKey = getKeyBytes(keyLen);
+
+        final IpSecAlgorithm ipsecEncryptAlgo =
+                new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CTR, cryptKey);
+        final EspCipher espCipher =
+                new EspCryptCipher(
+                        AES_CTR, AES_CTR_BLK_SIZE, cryptKey, AES_CTR_IV_LEN, AES_CTR_SALT_LEN);
+
+        runWithShellPermissionIdentity(new TestNetworkRunnable(new CheckCryptoImplTest(
+                ipsecEncryptAlgo, null /* ipsecAuthAlgo */, null /* ipsecAeadAlgo */,
+                espCipher, EspAuthNull.getInstance())));
+    }
+
+    @Test
+    public void testAesCtr160() throws Exception {
+        assumeTrue(hasIpSecAlgorithm(CRYPT_AES_CTR));
+
+        checkAesCtr(AES_CTR_KEY_LEN_20);
+    }
+
+    @Test
+    public void testAesCtr224() throws Exception {
+        assumeTrue(hasIpSecAlgorithm(CRYPT_AES_CTR));
+
+        checkAesCtr(AES_CTR_KEY_LEN_28);
+    }
+
+    @Test
+    public void testAesCtr288() throws Exception {
+        assumeTrue(hasIpSecAlgorithm(CRYPT_AES_CTR));
+
+        checkAesCtr(AES_CTR_KEY_LEN_36);
+    }
+
+    @Test
+    public void testAesXcbc() throws Exception {
+        assumeTrue(hasIpSecAlgorithm(AUTH_AES_XCBC));
+
+        final byte[] authKey = getKeyBytes(AES_XCBC_KEY_LEN);
+        final IpSecAlgorithm ipsecAuthAlgo =
+                new IpSecAlgorithm(IpSecAlgorithm.AUTH_AES_XCBC, authKey, AES_XCBC_ICV_LEN * 8);
+        final EspAuth espAuth = new EspAuth(AES_XCBC, authKey, AES_XCBC_ICV_LEN);
+
+        runWithShellPermissionIdentity(new TestNetworkRunnable(new CheckCryptoImplTest(
+                null /* ipsecEncryptAlgo */, ipsecAuthAlgo, null /* ipsecAeadAlgo */,
+                EspCipherNull.getInstance(), espAuth)));
+    }
+
+    @Test
+    public void testAesCmac() throws Exception {
+        assumeTrue(hasIpSecAlgorithm(AUTH_AES_CMAC));
+
+        final byte[] authKey = getKeyBytes(AES_CMAC_KEY_LEN);
+        final IpSecAlgorithm ipsecAuthAlgo =
+                new IpSecAlgorithm(IpSecAlgorithm.AUTH_AES_CMAC, authKey, AES_CMAC_ICV_LEN * 8);
+        final EspAuth espAuth = new EspAuth(AES_CMAC, authKey, AES_CMAC_ICV_LEN);
+
+        runWithShellPermissionIdentity(new TestNetworkRunnable(new CheckCryptoImplTest(
+                null /* ipsecEncryptAlgo */, ipsecAuthAlgo, null /* ipsecAeadAlgo */,
+                EspCipherNull.getInstance(), espAuth)));
+    }
+
+    @Test
+    public void testChaCha20Poly1305() throws Exception {
+        assumeTrue(hasIpSecAlgorithm(AUTH_CRYPT_CHACHA20_POLY1305));
+
+        final byte[] cryptKey = getKeyBytes(CHACHA20_POLY1305_KEY_LEN);
+        final IpSecAlgorithm ipsecAeadAlgo =
+                new IpSecAlgorithm(
+                        IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305,
+                        cryptKey,
+                        CHACHA20_POLY1305_ICV_LEN * 8);
+        final EspAeadCipher espAead =
+                new EspAeadCipher(
+                        CHACHA20_POLY1305,
+                        CHACHA20_POLY1305_BLK_SIZE,
+                        cryptKey,
+                        CHACHA20_POLY1305_IV_LEN,
+                        CHACHA20_POLY1305_ICV_LEN,
+                        CHACHA20_POLY1305_SALT_LEN);
+
+        runWithShellPermissionIdentity(
+                new TestNetworkRunnable(
+                        new CheckCryptoImplTest(
+                                null /* ipsecEncryptAlgo */,
+                                null /* ipsecAuthAlgo */,
+                                ipsecAeadAlgo,
+                                espAead,
+                                EspAuthNull.getInstance())));
+    }
+}
diff --git a/tests/cts/net/src/android/net/cts/IpSecManagerTest.java b/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
index e7e1d67..5c95aa3 100644
--- a/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
@@ -33,7 +33,7 @@
 import static android.net.cts.PacketUtils.AES_CMAC_KEY_LEN;
 import static android.net.cts.PacketUtils.AES_CTR_BLK_SIZE;
 import static android.net.cts.PacketUtils.AES_CTR_IV_LEN;
-import static android.net.cts.PacketUtils.AES_CTR_KEY_LEN;
+import static android.net.cts.PacketUtils.AES_CTR_KEY_LEN_20;
 import static android.net.cts.PacketUtils.AES_GCM_BLK_SIZE;
 import static android.net.cts.PacketUtils.AES_GCM_IV_LEN;
 import static android.net.cts.PacketUtils.AES_XCBC_ICV_LEN;
@@ -74,7 +74,6 @@
 
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
-import com.android.testutils.SkipPresubmit;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -771,7 +770,6 @@
     }
 
     @Test
-    @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
     public void testAesCbcHmacMd5Tcp6() throws Exception {
         IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
         IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_MD5, getKey(128), 96);
@@ -804,7 +802,6 @@
     }
 
     @Test
-    @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
     public void testAesCbcHmacSha1Tcp6() throws Exception {
         IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
         IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA1, getKey(160), 96);
@@ -837,7 +834,6 @@
     }
 
     @Test
-    @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
     public void testAesCbcHmacSha256Tcp6() throws Exception {
         IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
         IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, getKey(256), 128);
@@ -870,7 +866,6 @@
     }
 
     @Test
-    @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
     public void testAesCbcHmacSha384Tcp6() throws Exception {
         IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
         IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA384, getKey(384), 192);
@@ -903,7 +898,6 @@
     }
 
     @Test
-    @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
     public void testAesCbcHmacSha512Tcp6() throws Exception {
         IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
         IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA512, getKey(512), 256);
@@ -928,7 +922,7 @@
     }
 
     private static IpSecAlgorithm buildCryptAesCtr() throws Exception {
-        return new IpSecAlgorithm(CRYPT_AES_CTR, getKeyBytes(AES_CTR_KEY_LEN));
+        return new IpSecAlgorithm(CRYPT_AES_CTR, getKeyBytes(AES_CTR_KEY_LEN_20));
     }
 
     private static IpSecAlgorithm buildAuthHmacSha512() throws Exception {
@@ -947,7 +941,6 @@
     }
 
     @Test
-    @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
     public void testAesCtrHmacSha512Tcp6() throws Exception {
         assumeTrue(hasIpSecAlgorithm(CRYPT_AES_CTR));
 
@@ -1002,7 +995,6 @@
     }
 
     @Test
-    @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
     public void testAesCbcAesXCbcTcp6() throws Exception {
         assumeTrue(hasIpSecAlgorithm(AUTH_AES_XCBC));
 
@@ -1043,7 +1035,6 @@
     }
 
     @Test
-    @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
     public void testAesCbcAesCmacTcp6() throws Exception {
         assumeTrue(hasIpSecAlgorithm(AUTH_AES_CMAC));
 
@@ -1082,7 +1073,6 @@
     }
 
     @Test
-    @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
     public void testAesGcm64Tcp6() throws Exception {
         IpSecAlgorithm authCrypt =
                 new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 64);
@@ -1115,7 +1105,6 @@
     }
 
     @Test
-    @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
     public void testAesGcm96Tcp6() throws Exception {
         IpSecAlgorithm authCrypt =
                 new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 96);
@@ -1148,7 +1137,6 @@
     }
 
     @Test
-    @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
     public void testAesGcm128Tcp6() throws Exception {
         IpSecAlgorithm authCrypt =
                 new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
@@ -1187,7 +1175,6 @@
     }
 
     @Test
-    @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
     public void testChaCha20Poly1305Tcp6() throws Exception {
         assumeTrue(hasIpSecAlgorithm(AUTH_CRYPT_CHACHA20_POLY1305));
 
@@ -1463,7 +1450,6 @@
     }
 
     @Test
-    @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
     public void testCryptTcp6() throws Exception {
         IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
         checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, null, null, false, 1, false);
@@ -1471,7 +1457,6 @@
     }
 
     @Test
-    @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
     public void testAuthTcp6() throws Exception {
         IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, getKey(256), 128);
         checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, null, auth, null, false, 1, false);
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 8e2b310..9017f1b 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -70,6 +70,7 @@
 import android.os.Looper
 import android.os.Message
 import android.os.SystemClock
+import android.telephony.TelephonyManager
 import android.util.DebugUtils.valueToString
 import androidx.test.InstrumentationRegistry
 import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
@@ -362,15 +363,19 @@
         }
     }
 
-    private fun createConnectedNetworkAgent(context: Context = realContext, name: String? = null):
-            Pair<TestableNetworkAgent, TestableNetworkCallback> {
+    private fun createConnectedNetworkAgent(
+        context: Context = realContext,
+        name: String? = null,
+        initialConfig: NetworkAgentConfig? = null
+    ): Pair<TestableNetworkAgent, TestableNetworkCallback> {
         val request: NetworkRequest = NetworkRequest.Builder()
                 .clearCapabilities()
                 .addTransportType(TRANSPORT_TEST)
                 .build()
         val callback = TestableNetworkCallback(timeoutMs = DEFAULT_TIMEOUT_MS)
         requestNetwork(request, callback)
-        val agent = createNetworkAgent(context, name)
+        val config = initialConfig ?: NetworkAgentConfig.Builder().build()
+        val agent = createNetworkAgent(context, name, initialConfig = config)
         agent.setTeardownDelayMillis(0)
         agent.register()
         agent.markConnected()
@@ -383,6 +388,49 @@
     }
 
     @Test
+    fun testSetSubtypeNameAndExtraInfoByAgentConfig() {
+        val subtypeLTE = TelephonyManager.NETWORK_TYPE_LTE
+        val subtypeNameLTE = "LTE"
+        val legacyExtraInfo = "mylegacyExtraInfo"
+        val config = NetworkAgentConfig.Builder()
+                .setLegacySubType(subtypeLTE)
+                .setLegacySubTypeName(subtypeNameLTE)
+                .setLegacyExtraInfo(legacyExtraInfo).build()
+        val (agent, callback) = createConnectedNetworkAgent(initialConfig = config)
+        val networkInfo = mCM.getNetworkInfo(agent.network)
+        assertEquals(subtypeLTE, networkInfo.getSubtype())
+        assertEquals(subtypeNameLTE, networkInfo.getSubtypeName())
+        assertEquals(legacyExtraInfo, config.getLegacyExtraInfo())
+    }
+
+    @Test
+    fun testSetLegacySubtypeInNetworkAgent() {
+        val subtypeLTE = TelephonyManager.NETWORK_TYPE_LTE
+        val subtypeUMTS = TelephonyManager.NETWORK_TYPE_UMTS
+        val subtypeNameLTE = "LTE"
+        val subtypeNameUMTS = "UMTS"
+        val config = NetworkAgentConfig.Builder()
+                .setLegacySubType(subtypeLTE)
+                .setLegacySubTypeName(subtypeNameLTE).build()
+        val (agent, callback) = createConnectedNetworkAgent(initialConfig = config)
+            callback.expectAvailableThenValidatedCallbacks(agent.network)
+            agent.setLegacySubtype(subtypeUMTS, subtypeNameUMTS)
+
+            // There is no callback when networkInfo changes,
+            // so use the NetworkCapabilities callback to ensure
+            // that networkInfo is ready for verification.
+            val nc = NetworkCapabilities(agent.nc)
+            nc.addCapability(NET_CAPABILITY_NOT_METERED)
+            agent.sendNetworkCapabilities(nc)
+            callback.expectCapabilitiesThat(agent.network) {
+                it.hasCapability(NET_CAPABILITY_NOT_METERED)
+            }
+            val networkInfo = mCM.getNetworkInfo(agent.network)
+            assertEquals(subtypeUMTS, networkInfo.getSubtype())
+            assertEquals(subtypeNameUMTS, networkInfo.getSubtypeName())
+    }
+
+    @Test
     fun testConnectAndUnregister() {
         val (agent, callback) = createConnectedNetworkAgent()
         callback.expectAvailableThenValidatedCallbacks(agent.network)
diff --git a/tests/cts/net/src/android/net/cts/PacketUtils.java b/tests/cts/net/src/android/net/cts/PacketUtils.java
index 7e622f6..4d924d1 100644
--- a/tests/cts/net/src/android/net/cts/PacketUtils.java
+++ b/tests/cts/net/src/android/net/cts/PacketUtils.java
@@ -19,6 +19,10 @@
 import static android.system.OsConstants.IPPROTO_IPV6;
 import static android.system.OsConstants.IPPROTO_UDP;
 
+import android.util.ArraySet;
+
+import com.android.internal.net.ipsec.ike.crypto.AesXCbcImpl;
+
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
@@ -27,6 +31,7 @@
 import java.security.GeneralSecurityException;
 import java.security.SecureRandom;
 import java.util.Arrays;
+import java.util.Set;
 
 import javax.crypto.Cipher;
 import javax.crypto.Mac;
@@ -43,7 +48,9 @@
     static final int UDP_HDRLEN = 8;
     static final int TCP_HDRLEN = 20;
     static final int TCP_HDRLEN_WITH_TIMESTAMP_OPT = TCP_HDRLEN + 12;
+    static final int ESP_HDRLEN = 8;
     static final int ESP_BLK_SIZE = 4; // ESP has to be 4-byte aligned
+    static final int ESP_TRAILER_LEN = 2;
 
     // Not defined in OsConstants
     static final int IPPROTO_IPV4 = 4;
@@ -52,19 +59,25 @@
     // Encryption parameters
     static final int AES_CBC_IV_LEN = 16;
     static final int AES_CBC_BLK_SIZE = 16;
+    static final int AES_CTR_SALT_LEN = 4;
 
-    static final int AES_CTR_KEY_LEN = 20;
+    static final int AES_CTR_KEY_LEN_20 = 20;
+    static final int AES_CTR_KEY_LEN_28 = 28;
+    static final int AES_CTR_KEY_LEN_36 = 36;
     static final int AES_CTR_BLK_SIZE = ESP_BLK_SIZE;
     static final int AES_CTR_IV_LEN = 8;
 
     // AEAD parameters
     static final int AES_GCM_IV_LEN = 8;
     static final int AES_GCM_BLK_SIZE = 4;
+    static final int CHACHA20_POLY1305_KEY_LEN = 36;
     static final int CHACHA20_POLY1305_BLK_SIZE = ESP_BLK_SIZE;
     static final int CHACHA20_POLY1305_IV_LEN = 8;
+    static final int CHACHA20_POLY1305_SALT_LEN = 4;
     static final int CHACHA20_POLY1305_ICV_LEN = 16;
 
     // Authentication parameters
+    static final int HMAC_SHA256_ICV_LEN = 16;
     static final int HMAC_SHA512_KEY_LEN = 64;
     static final int HMAC_SHA512_ICV_LEN = 32;
     static final int AES_XCBC_KEY_LEN = 16;
@@ -72,10 +85,25 @@
     static final int AES_CMAC_KEY_LEN = 16;
     static final int AES_CMAC_ICV_LEN = 12;
 
+    // Block counter field should be 32 bits and starts from value one as per RFC 3686
+    static final byte[] AES_CTR_INITIAL_COUNTER = new byte[] {0x00, 0x00, 0x00, 0x01};
+
     // Encryption algorithms
     static final String AES = "AES";
     static final String AES_CBC = "AES/CBC/NoPadding";
+    static final String AES_CTR = "AES/CTR/NoPadding";
+
+    // AEAD algorithms
+    static final String CHACHA20_POLY1305 = "ChaCha20/Poly1305/NoPadding";
+
+    // Authentication algorithms
+    static final String HMAC_MD5 = "HmacMD5";
+    static final String HMAC_SHA1 = "HmacSHA1";
     static final String HMAC_SHA_256 = "HmacSHA256";
+    static final String HMAC_SHA_384 = "HmacSHA384";
+    static final String HMAC_SHA_512 = "HmacSHA512";
+    static final String AES_CMAC = "AESCMAC";
+    static final String AES_XCBC = "AesXCbc";
 
     public interface Payload {
         byte[] getPacketBytes(IpHeader header) throws Exception;
@@ -328,8 +356,9 @@
         public final int nextHeader;
         public final int spi;
         public final int seqNum;
-        public final byte[] key;
         public final byte[] payload;
+        public final EspCipher cipher;
+        public final EspAuth auth;
 
         /**
          * Generic constructor for ESP headers.
@@ -340,11 +369,48 @@
          * calculated using the pre-encryption IP header
          */
         public EspHeader(int nextHeader, int spi, int seqNum, byte[] key, byte[] payload) {
+            this(nextHeader, spi, seqNum, payload, getDefaultCipher(key), getDefaultAuth(key));
+        }
+
+        /**
+         * Generic constructor for ESP headers that allows configuring encryption and authentication
+         * algortihms.
+         *
+         * <p>For Tunnel mode, payload will be a full IP header + attached payloads
+         *
+         * <p>For Transport mode, payload will be only the attached payloads, but with the checksum
+         * calculated using the pre-encryption IP header
+         */
+        public EspHeader(
+                int nextHeader,
+                int spi,
+                int seqNum,
+                byte[] payload,
+                EspCipher cipher,
+                EspAuth auth) {
             this.nextHeader = nextHeader;
             this.spi = spi;
             this.seqNum = seqNum;
-            this.key = key;
             this.payload = payload;
+            this.cipher = cipher;
+            this.auth = auth;
+
+            if (cipher instanceof EspCipherNull && auth instanceof EspAuthNull) {
+                throw new IllegalArgumentException("No algorithm is provided");
+            }
+
+            if (cipher instanceof EspAeadCipher && !(auth instanceof EspAuthNull)) {
+                throw new IllegalArgumentException(
+                        "AEAD is provided with an authentication" + " algorithm.");
+            }
+        }
+
+        private static EspCipher getDefaultCipher(byte[] key) {
+            return new EspCryptCipher(AES_CBC, AES_CBC_BLK_SIZE, key, AES_CBC_IV_LEN);
+        }
+
+        private static EspAuth getDefaultAuth(byte[] key) {
+            return new EspAuth(HMAC_SHA_256, key, HMAC_SHA256_ICV_LEN);
         }
 
         public int getProtocolId() {
@@ -352,9 +418,10 @@
         }
 
         public short length() {
-            // ALWAYS uses AES-CBC, HMAC-SHA256 (128b trunc len)
-            return (short)
-                    calculateEspPacketSize(payload.length, AES_CBC_IV_LEN, AES_CBC_BLK_SIZE, 128);
+            final int icvLen =
+                    cipher instanceof EspAeadCipher ? ((EspAeadCipher) cipher).icvLen : auth.icvLen;
+            return calculateEspPacketSize(
+                    payload.length, cipher.ivLen, cipher.blockSize, icvLen * 8);
         }
 
         public byte[] getPacketBytes(IpHeader header) throws Exception {
@@ -368,58 +435,12 @@
             ByteBuffer espPayloadBuffer = ByteBuffer.allocate(DATA_BUFFER_LEN);
             espPayloadBuffer.putInt(spi);
             espPayloadBuffer.putInt(seqNum);
-            espPayloadBuffer.put(getCiphertext(key));
 
-            espPayloadBuffer.put(getIcv(getByteArrayFromBuffer(espPayloadBuffer)), 0, 16);
+            espPayloadBuffer.put(cipher.getCipherText(nextHeader, payload, spi, seqNum));
+            espPayloadBuffer.put(auth.getIcv(getByteArrayFromBuffer(espPayloadBuffer)));
+
             resultBuffer.put(getByteArrayFromBuffer(espPayloadBuffer));
         }
-
-        private byte[] getIcv(byte[] authenticatedSection) throws GeneralSecurityException {
-            Mac sha256HMAC = Mac.getInstance(HMAC_SHA_256);
-            SecretKeySpec authKey = new SecretKeySpec(key, HMAC_SHA_256);
-            sha256HMAC.init(authKey);
-
-            return sha256HMAC.doFinal(authenticatedSection);
-        }
-
-        /**
-         * Encrypts and builds ciphertext block. Includes the IV, Padding and Next-Header blocks
-         *
-         * <p>The ciphertext does NOT include the SPI/Sequence numbers, or the ICV.
-         */
-        private byte[] getCiphertext(byte[] key) throws GeneralSecurityException {
-            int paddedLen = calculateEspEncryptedLength(payload.length, AES_CBC_BLK_SIZE);
-            ByteBuffer paddedPayload = ByteBuffer.allocate(paddedLen);
-            paddedPayload.put(payload);
-
-            // Add padding - consecutive integers from 0x01
-            int pad = 1;
-            while (paddedPayload.position() < paddedPayload.limit()) {
-                paddedPayload.put((byte) pad++);
-            }
-
-            paddedPayload.position(paddedPayload.limit() - 2);
-            paddedPayload.put((byte) (paddedLen - 2 - payload.length)); // Pad length
-            paddedPayload.put((byte) nextHeader);
-
-            // Generate Initialization Vector
-            byte[] iv = new byte[AES_CBC_IV_LEN];
-            new SecureRandom().nextBytes(iv);
-            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
-            SecretKeySpec secretKeySpec = new SecretKeySpec(key, AES);
-
-            // Encrypt payload
-            Cipher cipher = Cipher.getInstance(AES_CBC);
-            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
-            byte[] encrypted = cipher.doFinal(getByteArrayFromBuffer(paddedPayload));
-
-            // Build ciphertext
-            ByteBuffer cipherText = ByteBuffer.allocate(AES_CBC_IV_LEN + encrypted.length);
-            cipherText.put(iv);
-            cipherText.put(encrypted);
-
-            return getByteArrayFromBuffer(cipherText);
-        }
     }
 
     private static int addAndWrapForChecksum(int currentChecksum, int value) {
@@ -436,15 +457,14 @@
         return (short) ((~val) & 0xffff);
     }
 
-    public static int calculateEspPacketSize(
+    public static short calculateEspPacketSize(
             int payloadLen, int cryptIvLength, int cryptBlockSize, int authTruncLen) {
-        final int ESP_HDRLEN = 4 + 4; // SPI + Seq#
         final int ICV_LEN = authTruncLen / 8; // Auth trailer; based on truncation length
-        payloadLen += cryptIvLength; // Initialization Vector
 
         // Align to block size of encryption algorithm
         payloadLen = calculateEspEncryptedLength(payloadLen, cryptBlockSize);
-        return payloadLen + ESP_HDRLEN + ICV_LEN;
+        payloadLen += cryptIvLength; // Initialization Vector
+        return (short) (payloadLen + ESP_HDRLEN + ICV_LEN);
     }
 
     private static int calculateEspEncryptedLength(int payloadLen, int cryptBlockSize) {
@@ -475,6 +495,237 @@
         }
     }
 
+    public abstract static class EspCipher {
+        protected static final int SALT_LEN_UNUSED = 0;
+
+        public final String algoName;
+        public final int blockSize;
+        public final byte[] key;
+        public final int ivLen;
+        public final int saltLen;
+        protected byte[] mIv;
+
+        public EspCipher(String algoName, int blockSize, byte[] key, int ivLen, int saltLen) {
+            this.algoName = algoName;
+            this.blockSize = blockSize;
+            this.key = key;
+            this.ivLen = ivLen;
+            this.saltLen = saltLen;
+            this.mIv = getIv(ivLen);
+        }
+
+        public void updateIv(byte[] iv) {
+            this.mIv = iv;
+        }
+
+        public static byte[] getPaddedPayload(int nextHeader, byte[] payload, int blockSize) {
+            final int paddedLen = calculateEspEncryptedLength(payload.length, blockSize);
+            final ByteBuffer paddedPayload = ByteBuffer.allocate(paddedLen);
+            paddedPayload.put(payload);
+
+            // Add padding - consecutive integers from 0x01
+            byte pad = 1;
+            while (paddedPayload.position() < paddedPayload.limit() - ESP_TRAILER_LEN) {
+                paddedPayload.put((byte) pad++);
+            }
+
+            // Add padding length and next header
+            paddedPayload.put((byte) (paddedLen - ESP_TRAILER_LEN - payload.length));
+            paddedPayload.put((byte) nextHeader);
+
+            return getByteArrayFromBuffer(paddedPayload);
+        }
+
+        private static byte[] getIv(int ivLen) {
+            final byte[] iv = new byte[ivLen];
+            new SecureRandom().nextBytes(iv);
+            return iv;
+        }
+
+        public abstract byte[] getCipherText(int nextHeader, byte[] payload, int spi, int seqNum)
+                throws GeneralSecurityException;
+    }
+
+    public static final class EspCipherNull extends EspCipher {
+        private static final String CRYPT_NULL = "CRYPT_NULL";
+        private static final int IV_LEN_UNUSED = 0;
+        private static final byte[] KEY_UNUSED = new byte[0];
+
+        private static final EspCipherNull sInstance = new EspCipherNull();
+
+        private EspCipherNull() {
+            super(CRYPT_NULL, ESP_BLK_SIZE, KEY_UNUSED, IV_LEN_UNUSED, SALT_LEN_UNUSED);
+        }
+
+        public static EspCipherNull getInstance() {
+            return sInstance;
+        }
+
+        @Override
+        public byte[] getCipherText(int nextHeader, byte[] payload, int spi, int seqNum)
+                throws GeneralSecurityException {
+            return getPaddedPayload(nextHeader, payload, blockSize);
+        }
+    }
+
+    public static final class EspCryptCipher extends EspCipher {
+        public EspCryptCipher(String algoName, int blockSize, byte[] key, int ivLen) {
+            this(algoName, blockSize, key, ivLen, SALT_LEN_UNUSED);
+        }
+
+        public EspCryptCipher(String algoName, int blockSize, byte[] key, int ivLen, int saltLen) {
+            super(algoName, blockSize, key, ivLen, saltLen);
+        }
+
+        @Override
+        public byte[] getCipherText(int nextHeader, byte[] payload, int spi, int seqNum)
+                throws GeneralSecurityException {
+            final IvParameterSpec ivParameterSpec;
+            final SecretKeySpec secretKeySpec;
+
+            if (AES_CBC.equals(algoName)) {
+                ivParameterSpec = new IvParameterSpec(mIv);
+                secretKeySpec = new SecretKeySpec(key, algoName);
+            } else if (AES_CTR.equals(algoName)) {
+                // Provided key consists of encryption/decryption key plus 4-byte salt. Salt is used
+                // with ESP payload IV and initial block counter value to build IvParameterSpec.
+                final byte[] secretKey = Arrays.copyOfRange(key, 0, key.length - saltLen);
+                final byte[] salt = Arrays.copyOfRange(key, secretKey.length, key.length);
+                secretKeySpec = new SecretKeySpec(secretKey, algoName);
+
+                final ByteBuffer ivParameterBuffer =
+                        ByteBuffer.allocate(mIv.length + saltLen + AES_CTR_INITIAL_COUNTER.length);
+                ivParameterBuffer.put(salt);
+                ivParameterBuffer.put(mIv);
+                ivParameterBuffer.put(AES_CTR_INITIAL_COUNTER);
+                ivParameterSpec = new IvParameterSpec(ivParameterBuffer.array());
+            } else {
+                throw new IllegalArgumentException("Invalid algorithm " + algoName);
+            }
+
+            // Encrypt payload
+            final Cipher cipher = Cipher.getInstance(algoName);
+            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
+            final byte[] encrypted =
+                    cipher.doFinal(getPaddedPayload(nextHeader, payload, blockSize));
+
+            // Build ciphertext
+            final ByteBuffer cipherText = ByteBuffer.allocate(mIv.length + encrypted.length);
+            cipherText.put(mIv);
+            cipherText.put(encrypted);
+
+            return getByteArrayFromBuffer(cipherText);
+        }
+    }
+
+    public static final class EspAeadCipher extends EspCipher {
+        public final int icvLen;
+
+        public EspAeadCipher(
+                String algoName, int blockSize, byte[] key, int ivLen, int icvLen, int saltLen) {
+            super(algoName, blockSize, key, ivLen, saltLen);
+            this.icvLen = icvLen;
+        }
+
+        @Override
+        public byte[] getCipherText(int nextHeader, byte[] payload, int spi, int seqNum)
+                throws GeneralSecurityException {
+            // Provided key consists of encryption/decryption key plus salt. Salt is used
+            // with ESP payload IV to build IvParameterSpec.
+            final byte[] secretKey = Arrays.copyOfRange(key, 0, key.length - saltLen);
+            final byte[] salt = Arrays.copyOfRange(key, secretKey.length, key.length);
+
+            final SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, algoName);
+
+            final ByteBuffer ivParameterBuffer = ByteBuffer.allocate(saltLen + mIv.length);
+            ivParameterBuffer.put(salt);
+            ivParameterBuffer.put(mIv);
+            final IvParameterSpec ivParameterSpec = new IvParameterSpec(ivParameterBuffer.array());
+
+            final ByteBuffer aadBuffer = ByteBuffer.allocate(ESP_HDRLEN);
+            aadBuffer.putInt(spi);
+            aadBuffer.putInt(seqNum);
+
+            // Encrypt payload
+            final Cipher cipher = Cipher.getInstance(algoName);
+            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
+            cipher.updateAAD(aadBuffer.array());
+            final byte[] encryptedTextAndIcv =
+                    cipher.doFinal(getPaddedPayload(nextHeader, payload, blockSize));
+
+            // Build ciphertext
+            final ByteBuffer cipherText =
+                    ByteBuffer.allocate(mIv.length + encryptedTextAndIcv.length);
+            cipherText.put(mIv);
+            cipherText.put(encryptedTextAndIcv);
+
+            return getByteArrayFromBuffer(cipherText);
+        }
+    }
+
+    public static class EspAuth {
+        public final String algoName;
+        public final byte[] key;
+        public final int icvLen;
+
+        private static final Set<String> JCE_SUPPORTED_MACS = new ArraySet<>();
+
+        static {
+            JCE_SUPPORTED_MACS.add(HMAC_MD5);
+            JCE_SUPPORTED_MACS.add(HMAC_SHA1);
+            JCE_SUPPORTED_MACS.add(HMAC_SHA_256);
+            JCE_SUPPORTED_MACS.add(HMAC_SHA_384);
+            JCE_SUPPORTED_MACS.add(HMAC_SHA_512);
+            JCE_SUPPORTED_MACS.add(AES_CMAC);
+        }
+
+        public EspAuth(String algoName, byte[] key, int icvLen) {
+            this.algoName = algoName;
+            this.key = key;
+            this.icvLen = icvLen;
+        }
+
+        public byte[] getIcv(byte[] authenticatedSection) throws GeneralSecurityException {
+            if (AES_XCBC.equals(algoName)) {
+                final Cipher aesCipher = Cipher.getInstance(AES_CBC);
+                return new AesXCbcImpl().mac(key, authenticatedSection, true /* needTruncation */);
+            } else if (JCE_SUPPORTED_MACS.contains(algoName)) {
+                final Mac mac = Mac.getInstance(algoName);
+                final SecretKeySpec authKey = new SecretKeySpec(key, algoName);
+                mac.init(authKey);
+
+                final ByteBuffer buffer = ByteBuffer.wrap(mac.doFinal(authenticatedSection));
+                final byte[] icv = new byte[icvLen];
+                buffer.get(icv);
+                return icv;
+            } else {
+                throw new IllegalArgumentException("Invalid algorithm: " + algoName);
+            }
+        }
+    }
+
+    public static final class EspAuthNull extends EspAuth {
+        private static final String AUTH_NULL = "AUTH_NULL";
+        private static final int ICV_LEN_UNUSED = 0;
+        private static final byte[] KEY_UNUSED = new byte[0];
+        private static final byte[] ICV_EMPTY = new byte[0];
+
+        private static final EspAuthNull sInstance = new EspAuthNull();
+
+        private EspAuthNull() {
+            super(AUTH_NULL, KEY_UNUSED, ICV_LEN_UNUSED);
+        }
+
+        public static EspAuthNull getInstance() {
+            return sInstance;
+        }
+
+        @Override
+        public byte[] getIcv(byte[] authenticatedSection) throws GeneralSecurityException {
+            return ICV_EMPTY;
+        }
+    }
+
     /*
      * Debug printing
      */
diff --git a/tests/cts/net/src/android/net/cts/TestNetworkRunnable.java b/tests/cts/net/src/android/net/cts/TestNetworkRunnable.java
new file mode 100644
index 0000000..0eb5644
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/TestNetworkRunnable.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.cts;
+
+import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.cts.util.CtsNetUtils.TestNetworkCallback;
+
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.LinkAddress;
+import android.net.Network;
+import android.net.TestNetworkInterface;
+import android.net.TestNetworkManager;
+import android.net.cts.util.CtsNetUtils;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.ThrowingRunnable;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+
+/** This class supports running a test with a test network. */
+public class TestNetworkRunnable implements ThrowingRunnable {
+    private static final int IP4_PREFIX_LEN = 32;
+    private static final int IP6_PREFIX_LEN = 128;
+
+    private static final InetAddress DEFAULT_ADDRESS_4 =
+            InetAddress.parseNumericAddress("192.2.0.2");
+    private static final InetAddress DEFAULT_ADDRESS_6 =
+            InetAddress.parseNumericAddress("2001:db8:1::2");
+
+    private static final Context sContext = InstrumentationRegistry.getContext();
+    private static final ConnectivityManager sCm =
+            sContext.getSystemService(ConnectivityManager.class);
+
+    private final Test mTest;
+
+    public TestNetworkRunnable(Test test) {
+        mTest = test;
+    }
+
+    private void runTest() throws Exception {
+        final TestNetworkManager tnm = sContext.getSystemService(TestNetworkManager.class);
+
+        // Non-final; these variables ensure we clean up properly after our test if we
+        // have allocated test network resources
+        TestNetworkInterface testIface = null;
+        TestNetworkCallback tunNetworkCallback = null;
+
+        final CtsNetUtils ctsNetUtils = new CtsNetUtils(sContext);
+        final InetAddress[] addresses = mTest.getTestNetworkAddresses();
+        final LinkAddress[] linkAddresses = new LinkAddress[addresses.length];
+        for (int i = 0; i < addresses.length; i++) {
+            InetAddress address = addresses[i];
+            if (address instanceof Inet4Address) {
+                linkAddresses[i] = new LinkAddress(address, IP4_PREFIX_LEN);
+            } else {
+                linkAddresses[i] = new LinkAddress(address, IP6_PREFIX_LEN);
+            }
+        }
+
+        try {
+            // Build underlying test network
+            testIface = tnm.createTunInterface(linkAddresses);
+
+            // Hold on to this callback to ensure network does not get reaped.
+            tunNetworkCallback = ctsNetUtils.setupAndGetTestNetwork(testIface.getInterfaceName());
+
+            mTest.runTest(testIface, tunNetworkCallback);
+        } finally {
+            try {
+                mTest.cleanupTest();
+            } catch (Exception e) {
+                // No action
+            }
+
+            if (testIface != null) {
+                testIface.getFileDescriptor().close();
+            }
+
+            if (tunNetworkCallback != null) {
+                sCm.unregisterNetworkCallback(tunNetworkCallback);
+            }
+
+            final Network testNetwork = tunNetworkCallback.currentNetwork;
+            if (testNetwork != null) {
+                tnm.teardownTestNetwork(testNetwork);
+            }
+        }
+    }
+
+    @Override
+    public void run() throws Exception {
+        if (sContext.checkSelfPermission(MANAGE_TEST_NETWORKS) == PERMISSION_GRANTED) {
+            runTest();
+        } else {
+            runWithShellPermissionIdentity(this::runTest, MANAGE_TEST_NETWORKS);
+        }
+    }
+
+    /** Interface for test caller to configure the test that will be run with a test network */
+    public interface Test {
+        /** Runs the test with a test network */
+        void runTest(TestNetworkInterface testIface, TestNetworkCallback tunNetworkCallback)
+                throws Exception;
+
+        /** Cleans up when the test is finished or interrupted */
+        void cleanupTest();
+
+        /** Returns the IP addresses that will be used by the test network */
+        default InetAddress[] getTestNetworkAddresses() {
+            return new InetAddress[] {DEFAULT_ADDRESS_4, DEFAULT_ADDRESS_6};
+        }
+    }
+}
diff --git a/tests/cts/net/src/android/net/cts/TunUtils.java b/tests/cts/net/src/android/net/cts/TunUtils.java
index 7887385..d8e39b4 100644
--- a/tests/cts/net/src/android/net/cts/TunUtils.java
+++ b/tests/cts/net/src/android/net/cts/TunUtils.java
@@ -147,6 +147,10 @@
         return espPkt; // We've found the packet we're looking for.
     }
 
+    public byte[] awaitEspPacket(int spi, boolean useEncap) throws Exception {
+        return awaitPacket((pkt) -> isEsp(pkt, spi, useEncap));
+    }
+
     private static boolean isSpiEqual(byte[] pkt, int espOffset, int spi) {
         // Check SPI byte by byte.
         return pkt[espOffset] == (byte) ((spi >>> 24) & 0xff)
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index 6b7921b..6c4bb90 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -56,21 +56,21 @@
         "java/**/*.kt",
     ],
     test_suites: ["device-tests"],
+    certificate: "platform",
     jarjar_rules: "jarjar-rules.txt",
     static_libs: [
         "androidx.test.rules",
         "bouncycastle-repackaged-unbundled",
+        "FrameworksNetCommonTests",
         "frameworks-base-testutils",
         "frameworks-net-integration-testutils",
         "framework-protos",
-        "mockito-target-extended-minus-junit4",
+        "mockito-target-minus-junit4",
         "net-tests-utils",
         "platform-test-annotations",
         "service-connectivity-pre-jarjar",
         "services.core",
         "services.net",
-        // At the bottom to prefer test libraries above instead of those in the common tests
-        "FrameworksNetCommonTests",
     ],
     libs: [
         "android.net.ipsec.ike.stubs.module_lib",
@@ -80,9 +80,6 @@
         "ServiceConnectivityResources",
     ],
     jni_libs: [
-        // For mockito extended
-        "libdexmakerjvmtiagent",
-        "libstaticjvmtiagent",
         "libservice-connectivity",
     ],
 }
diff --git a/tests/unit/AndroidManifest.xml b/tests/unit/AndroidManifest.xml
index 18653dc..4c60ccf 100644
--- a/tests/unit/AndroidManifest.xml
+++ b/tests/unit/AndroidManifest.xml
@@ -42,7 +42,7 @@
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
     <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
     <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
-    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
+    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
     <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
     <uses-permission android:name="android.permission.NETWORK_STACK" />
     <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
@@ -50,8 +50,7 @@
     <uses-permission android:name="android.permission.NETWORK_STATS_PROVIDER" />
     <uses-permission android:name="android.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE" />
 
-    <!-- Debuggable is necessary for mockito extended -->
-    <application android:debuggable="true">
+    <application>
         <uses-library android:name="android.test.runner" />
         <uses-library android:name="android.net.ipsec.ike" />
     </application>
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/ConnectivityManagerTest.java b/tests/unit/java/android/net/ConnectivityManagerTest.java
index b1b4218..591e0cc 100644
--- a/tests/unit/java/android/net/ConnectivityManagerTest.java
+++ b/tests/unit/java/android/net/ConnectivityManagerTest.java
@@ -46,7 +46,6 @@
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -217,7 +216,6 @@
         ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
         NetworkRequest request = makeRequest(1);
         NetworkCallback callback = mock(ConnectivityManager.NetworkCallback.class);
-        doCallRealMethod().when(callback).onAvailable(any(), any(), any(), anyInt());
         Handler handler = new Handler(Looper.getMainLooper());
         ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class);
 
@@ -246,7 +244,6 @@
         NetworkRequest req1 = makeRequest(1);
         NetworkRequest req2 = makeRequest(2);
         NetworkCallback callback = mock(ConnectivityManager.NetworkCallback.class);
-        doCallRealMethod().when(callback).onAvailable(any(), any(), any(), anyInt());
         Handler handler = new Handler(Looper.getMainLooper());
         ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class);
 
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/android/net/nsd/NsdManagerTest.java b/tests/unit/java/android/net/nsd/NsdManagerTest.java
index 370179c..b0a9b8a 100644
--- a/tests/unit/java/android/net/nsd/NsdManagerTest.java
+++ b/tests/unit/java/android/net/nsd/NsdManagerTest.java
@@ -20,12 +20,12 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.os.Handler;
@@ -66,7 +66,7 @@
         MockitoAnnotations.initMocks(this);
 
         mServiceHandler = spy(MockServiceHandler.create(mContext));
-        doReturn(new Messenger(mServiceHandler)).when(mService).getMessenger();
+        when(mService.getMessenger()).thenReturn(new Messenger(mServiceHandler));
 
         mManager = makeManager();
     }
diff --git a/tests/unit/java/android/net/util/MultinetworkPolicyTrackerTest.kt b/tests/unit/java/android/net/util/MultinetworkPolicyTrackerTest.kt
index 9291532..25aa626 100644
--- a/tests/unit/java/android/net/util/MultinetworkPolicyTrackerTest.kt
+++ b/tests/unit/java/android/net/util/MultinetworkPolicyTrackerTest.kt
@@ -74,7 +74,6 @@
         doReturn(Context.TELEPHONY_SERVICE).`when`(it)
                 .getSystemServiceName(TelephonyManager::class.java)
         doReturn(telephonyManager).`when`(it).getSystemService(Context.TELEPHONY_SERVICE)
-        doReturn(telephonyManager).`when`(it).getSystemService(TelephonyManager::class.java)
         doReturn(subscriptionManager).`when`(it)
                 .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
         doReturn(resolver).`when`(it).contentResolver
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 2c19cb8..e8f249e 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -18,14 +18,10 @@
 
 import static android.Manifest.permission.CHANGE_NETWORK_STATE;
 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
-import static android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE;
 import static android.Manifest.permission.DUMP;
-import static android.Manifest.permission.GET_INTENT_SENDER_INTENT;
 import static android.Manifest.permission.LOCAL_MAC_ADDRESS;
 import static android.Manifest.permission.NETWORK_FACTORY;
 import static android.Manifest.permission.NETWORK_SETTINGS;
-import static android.Manifest.permission.NETWORK_STACK;
-import static android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD;
 import static android.app.PendingIntent.FLAG_IMMUTABLE;
 import static android.content.Intent.ACTION_PACKAGE_ADDED;
 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
@@ -113,6 +109,8 @@
 import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK;
 import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY;
 import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY;
+import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST;
+import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST_ONLY;
 import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_UNINITIALIZED;
 import static android.net.RouteInfo.RTN_UNREACHABLE;
 import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.PREFIX_OPERATION_ADDED;
@@ -122,6 +120,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;
@@ -134,7 +134,6 @@
 import static com.android.testutils.MiscAsserts.assertRunsInAtMost;
 import static com.android.testutils.MiscAsserts.assertSameElements;
 import static com.android.testutils.MiscAsserts.assertThrows;
-import static com.android.testutils.TestPermissionUtil.runAsShell;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -260,7 +259,6 @@
 import android.net.shared.PrivateDnsConfig;
 import android.net.util.MultinetworkPolicyTracker;
 import android.os.BadParcelableException;
-import android.os.BatteryStatsManager;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -462,6 +460,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;
@@ -469,7 +468,6 @@
     @Mock DeviceIdleInternal mDeviceIdleInternal;
     @Mock INetworkManagementService mNetworkManagementService;
     @Mock NetworkStatsManager mStatsManager;
-    @Mock BatteryStatsManager mBatteryStatsManager;
     @Mock IDnsResolver mMockDnsResolver;
     @Mock INetd mMockNetd;
     @Mock NetworkStackClientBase mNetworkStack;
@@ -488,6 +486,7 @@
     @Mock VpnProfileStore mVpnProfileStore;
     @Mock SystemConfigManager mSystemConfigManager;
     @Mock Resources mResources;
+    @Mock ProxyTracker mProxyTracker;
 
     private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
             ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -580,7 +579,6 @@
             if (Context.NETWORK_POLICY_SERVICE.equals(name)) return mNetworkPolicyManager;
             if (Context.SYSTEM_CONFIG_SERVICE.equals(name)) return mSystemConfigManager;
             if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager;
-            if (Context.BATTERY_STATS_SERVICE.equals(name)) return mBatteryStatsManager;
             return super.getSystemService(name);
         }
 
@@ -661,13 +659,6 @@
         public void setPermission(String permission, Integer granted) {
             mMockedPermissions.put(permission, granted);
         }
-
-        @Override
-        public Intent registerReceiverForAllUsers(@Nullable BroadcastReceiver receiver,
-                @NonNull IntentFilter filter, @Nullable String broadcastPermission,
-                @Nullable Handler scheduler) {
-            return null;
-        }
     }
 
     private void waitForIdle() {
@@ -1217,22 +1208,7 @@
                             return mDeviceIdleInternal;
                         }
                     },
-                    mNetworkManagementService, mMockNetd, userId, mVpnProfileStore,
-                    new SystemServices(mServiceContext) {
-                        @Override
-                        public String settingsSecureGetStringForUser(String key, int userId) {
-                            switch (key) {
-                                // Settings keys not marked as @Readable are not readable from
-                                // non-privileged apps, unless marked as testOnly=true
-                                // (atest refuses to install testOnly=true apps), even if mocked
-                                // in the content provider.
-                                case Settings.Secure.ALWAYS_ON_VPN_APP:
-                                    return null;
-                                default:
-                                    return super.settingsSecureGetStringForUser(key, userId);
-                            }
-                        }
-                    }, new Ikev2SessionCreator());
+                    mNetworkManagementService, mMockNetd, userId, mVpnProfileStore);
         }
 
         public void setUids(Set<UidRange> uids) {
@@ -1305,10 +1281,14 @@
             return mMockNetworkAgent;
         }
 
-        public void establish(LinkProperties lp, int uid, Set<UidRange> ranges, boolean validated,
-                boolean hasInternet, boolean isStrictMode) throws Exception {
+        private void setOwnerAndAdminUid(int uid) throws Exception {
             mNetworkCapabilities.setOwnerUid(uid);
             mNetworkCapabilities.setAdministratorUids(new int[]{uid});
+        }
+
+        public void establish(LinkProperties lp, int uid, Set<UidRange> ranges, boolean validated,
+                boolean hasInternet, boolean isStrictMode) throws Exception {
+            setOwnerAndAdminUid(uid);
             registerAgent(false, ranges, lp);
             connect(validated, hasInternet, isStrictMode);
             waitForIdle();
@@ -1553,6 +1533,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);
@@ -1561,8 +1543,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, "",
@@ -1610,11 +1592,6 @@
         mServiceContext = new MockContext(InstrumentationRegistry.getContext(),
                 new FakeSettingsProvider());
         mServiceContext.setUseRegisteredHandlers(true);
-        mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_GRANTED);
-        mServiceContext.setPermission(NETWORK_STACK, PERMISSION_GRANTED);
-        mServiceContext.setPermission(CONTROL_OEM_PAID_NETWORK_PREFERENCE, PERMISSION_GRANTED);
-        mServiceContext.setPermission(PACKET_KEEPALIVE_OFFLOAD, PERMISSION_GRANTED);
-        mServiceContext.setPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, PERMISSION_GRANTED);
 
         mAlarmManagerThread = new HandlerThread("TestAlarmManager");
         mAlarmManagerThread.start();
@@ -1666,7 +1643,7 @@
         doReturn(mNetIdManager).when(deps).makeNetIdManager();
         doReturn(mNetworkStack).when(deps).getNetworkStack();
         doReturn(mSystemProperties).when(deps).getSystemProperties();
-        doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
+        doReturn(mProxyTracker).when(deps).makeProxyTracker(any(), any());
         doReturn(true).when(deps).queryUserAccess(anyInt(), any(), any());
         doAnswer(inv -> {
             mPolicyTracker = new WrappedMultinetworkPolicyTracker(
@@ -1845,16 +1822,6 @@
         assertEmpty(mCm.getAllNetworkStateSnapshots());
     }
 
-    private static PendingIntent wrapPendingIntent(final PendingIntent intent) {
-        final PendingIntent ret = spy(intent);
-        // intentFilterEquals requires GET_INTENT_SENDER_INTENT permission
-        doAnswer(inv -> {
-            final PendingIntent other = inv.getArgument(0);
-            return runAsShell(GET_INTENT_SENDER_INTENT, () -> intent.intentFilterEquals(other));
-        }).when(ret).intentFilterEquals(any());
-        return ret;
-    }
-
     /**
      * Class to simplify expecting broadcasts using BroadcastInterceptingContext.
      * Ensures that the receiver is unregistered after the expected broadcast is received. This
@@ -3303,8 +3270,8 @@
 
     @Test
     public void testNoMutableNetworkRequests() throws Exception {
-        final PendingIntent pendingIntent = wrapPendingIntent(PendingIntent.getBroadcast(
-                mContext, 0 /* requestCode */, new Intent("a"), FLAG_IMMUTABLE));
+        final PendingIntent pendingIntent = PendingIntent.getBroadcast(
+                mContext, 0 /* requestCode */, new Intent("a"), FLAG_IMMUTABLE);
         NetworkRequest request1 = new NetworkRequest.Builder()
                 .addCapability(NET_CAPABILITY_VALIDATED)
                 .build();
@@ -4148,16 +4115,16 @@
                 mCm.registerNetworkCallback(r, new NetworkCallback()));
 
         assertThrows(SecurityException.class, () ->
-                mCm.registerNetworkCallback(r, wrapPendingIntent(PendingIntent.getService(
-                        mServiceContext, 0 /* requestCode */, new Intent(), FLAG_IMMUTABLE))));
+                mCm.registerNetworkCallback(r, PendingIntent.getService(
+                        mServiceContext, 0 /* requestCode */, new Intent(), FLAG_IMMUTABLE)));
 
         // Requesting a Network with signal strength should get IllegalArgumentException.
         assertThrows(IllegalArgumentException.class, () ->
                 mCm.requestNetwork(r, new NetworkCallback()));
 
         assertThrows(IllegalArgumentException.class, () ->
-                mCm.requestNetwork(r, wrapPendingIntent(PendingIntent.getService(
-                        mServiceContext, 0 /* requestCode */, new Intent(), FLAG_IMMUTABLE))));
+                mCm.requestNetwork(r, PendingIntent.getService(
+                        mServiceContext, 0 /* requestCode */, new Intent(), FLAG_IMMUTABLE)));
     }
 
     @Test
@@ -5713,14 +5680,14 @@
         }
         j = 0;
         while (j++ < INTENTS / 2) {
-            final PendingIntent pi = wrapPendingIntent(PendingIntent.getBroadcast(mContext,
-                    0 /* requestCode */, new Intent("a" + j), FLAG_IMMUTABLE));
+            final PendingIntent pi = PendingIntent.getBroadcast(mContext, 0 /* requestCode */,
+                    new Intent("a" + j), FLAG_IMMUTABLE);
             mCm.requestNetwork(networkRequest, pi);
             registered.add(pi);
         }
         while (j++ < INTENTS) {
-            final PendingIntent pi = wrapPendingIntent(PendingIntent.getBroadcast(mContext,
-                    0 /* requestCode */, new Intent("b" + j), FLAG_IMMUTABLE));
+            final PendingIntent pi = PendingIntent.getBroadcast(mContext, 0 /* requestCode */,
+                    new Intent("b" + j), FLAG_IMMUTABLE);
             mCm.registerNetworkCallback(networkRequest, pi);
             registered.add(pi);
         }
@@ -5734,13 +5701,13 @@
         );
         assertThrows(TooManyRequestsException.class, () ->
                 mCm.requestNetwork(networkRequest,
-                        wrapPendingIntent(PendingIntent.getBroadcast(mContext, 0 /* requestCode */,
-                                new Intent("c"), FLAG_IMMUTABLE)))
+                        PendingIntent.getBroadcast(mContext, 0 /* requestCode */,
+                                new Intent("c"), FLAG_IMMUTABLE))
         );
         assertThrows(TooManyRequestsException.class, () ->
                 mCm.registerNetworkCallback(networkRequest,
-                        wrapPendingIntent(PendingIntent.getBroadcast(mContext, 0 /* requestCode */,
-                                new Intent("d"), FLAG_IMMUTABLE)))
+                        PendingIntent.getBroadcast(mContext, 0 /* requestCode */,
+                                new Intent("d"), FLAG_IMMUTABLE))
         );
 
         // The system gets another SYSTEM_ONLY_MAX_REQUESTS slots.
@@ -5820,16 +5787,16 @@
         waitForIdle();
 
         for (int i = 0; i < MAX_REQUESTS; i++) {
-            final PendingIntent pendingIntent = wrapPendingIntent(PendingIntent.getBroadcast(
-                    mContext, 0 /* requestCode */, new Intent("e" + i), FLAG_IMMUTABLE));
+            final PendingIntent pendingIntent = PendingIntent.getBroadcast(
+                    mContext, 0 /* requestCode */, new Intent("e" + i), FLAG_IMMUTABLE);
             mCm.requestNetwork(networkRequest, pendingIntent);
             mCm.unregisterNetworkCallback(pendingIntent);
         }
         waitForIdle();
 
         for (int i = 0; i < MAX_REQUESTS; i++) {
-            final PendingIntent pendingIntent = wrapPendingIntent(PendingIntent.getBroadcast(
-                    mContext, 0 /* requestCode */, new Intent("f" + i), FLAG_IMMUTABLE));
+            final PendingIntent pendingIntent = PendingIntent.getBroadcast(
+                    mContext, 0 /* requestCode */, new Intent("f" + i), FLAG_IMMUTABLE);
             mCm.registerNetworkCallback(networkRequest, pendingIntent);
             mCm.unregisterNetworkCallback(pendingIntent);
         }
@@ -9272,7 +9239,8 @@
         mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
                 PERMISSION_DENIED);
         mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_DENIED);
-        mServiceContext.setPermission(NETWORK_STACK, PERMISSION_DENIED);
+        mServiceContext.setPermission(Manifest.permission.NETWORK_STACK,
+                PERMISSION_DENIED);
         mServiceContext.setPermission(Manifest.permission.NETWORK_SETUP_WIZARD,
                 PERMISSION_DENIED);
     }
@@ -9713,7 +9681,7 @@
         setupConnectionOwnerUid(vpnOwnerUid, vpnType);
 
         // Test as VPN app
-        mServiceContext.setPermission(NETWORK_STACK, PERMISSION_DENIED);
+        mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
         mServiceContext.setPermission(
                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_DENIED);
     }
@@ -9753,7 +9721,8 @@
     public void testGetConnectionOwnerUidVpnServiceNetworkStackDoesNotThrow() throws Exception {
         final int myUid = Process.myUid();
         setupConnectionOwnerUid(myUid, VpnManager.TYPE_VPN_SERVICE);
-        mServiceContext.setPermission(NETWORK_STACK, PERMISSION_GRANTED);
+        mServiceContext.setPermission(
+                android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
 
         assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo()));
     }
@@ -9921,7 +9890,8 @@
     public void testCheckConnectivityDiagnosticsPermissionsNetworkStack() throws Exception {
         final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities());
 
-        mServiceContext.setPermission(NETWORK_STACK, PERMISSION_GRANTED);
+        mServiceContext.setPermission(
+                android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
         assertTrue(
                 "NetworkStack permission not applied",
                 mService.checkConnectivityDiagnosticsPermissions(
@@ -9937,7 +9907,7 @@
         nc.setAdministratorUids(new int[] {wrongUid});
         final NetworkAgentInfo naiWithUid = fakeWifiNai(nc);
 
-        mServiceContext.setPermission(NETWORK_STACK, PERMISSION_DENIED);
+        mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
 
         assertFalse(
                 "Mismatched uid/package name should not pass the location permission check",
@@ -9947,7 +9917,7 @@
 
     private void verifyConnectivityDiagnosticsPermissionsWithNetworkAgentInfo(
             NetworkAgentInfo info, boolean expectPermission) {
-        mServiceContext.setPermission(NETWORK_STACK, PERMISSION_DENIED);
+        mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
 
         assertEquals(
                 "Unexpected ConnDiags permission",
@@ -10015,7 +9985,7 @@
 
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
-        mServiceContext.setPermission(NETWORK_STACK, PERMISSION_DENIED);
+        mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
 
         assertTrue(
                 "NetworkCapabilities administrator uid permission not applied",
@@ -10032,7 +10002,7 @@
 
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
-        mServiceContext.setPermission(NETWORK_STACK, PERMISSION_DENIED);
+        mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
 
         // Use wrong pid and uid
         assertFalse(
@@ -10058,7 +10028,8 @@
         final NetworkRequest request = new NetworkRequest.Builder().build();
         when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
 
-        mServiceContext.setPermission(NETWORK_STACK, PERMISSION_GRANTED);
+        mServiceContext.setPermission(
+                android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
 
         mService.registerConnectivityDiagnosticsCallback(
                 mConnectivityDiagnosticsCallback, request, mContext.getPackageName());
@@ -10077,7 +10048,8 @@
         final NetworkRequest request = new NetworkRequest.Builder().build();
         when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
 
-        mServiceContext.setPermission(NETWORK_STACK, PERMISSION_GRANTED);
+        mServiceContext.setPermission(
+                android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
 
         mService.registerConnectivityDiagnosticsCallback(
                 mConnectivityDiagnosticsCallback, request, mContext.getPackageName());
@@ -10297,16 +10269,23 @@
 
     @Test
     public void testVpnUidRangesUpdate() throws Exception {
-        LinkProperties lp = new LinkProperties();
+        // Set up a WiFi network without proxy.
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(true);
+        assertNull(mService.getProxyForNetwork(null));
+        assertNull(mCm.getDefaultProxy());
+
+        final LinkProperties lp = new LinkProperties();
         lp.setInterfaceName("tun0");
         lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
         final UidRange vpnRange = PRIMARY_UIDRANGE;
-        Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
+        final Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
         mMockVpn.establish(lp, VPN_UID, vpnRanges);
         assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
+        // VPN is connected but proxy is not set, so there is no need to send proxy broadcast.
+        verify(mProxyTracker, never()).sendProxyBroadcast();
 
-        reset(mMockNetd);
         // Update to new range which is old range minus APP1, i.e. only APP2
         final Set<UidRange> newRanges = new HashSet<>(Arrays.asList(
                 new UidRange(vpnRange.start, APP1_UID - 1),
@@ -10316,6 +10295,101 @@
 
         assertVpnUidRangesUpdated(true, newRanges, VPN_UID);
         assertVpnUidRangesUpdated(false, vpnRanges, VPN_UID);
+
+        // Uid has changed but proxy is not set, so there is no need to send proxy broadcast.
+        verify(mProxyTracker, never()).sendProxyBroadcast();
+
+        final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
+        lp.setHttpProxy(testProxyInfo);
+        mMockVpn.sendLinkProperties(lp);
+        waitForIdle();
+        // Proxy is set, so send a proxy broadcast.
+        verify(mProxyTracker, times(1)).sendProxyBroadcast();
+        reset(mProxyTracker);
+
+        mMockVpn.setUids(vpnRanges);
+        waitForIdle();
+        // Uid has changed and proxy is already set, so send a proxy broadcast.
+        verify(mProxyTracker, times(1)).sendProxyBroadcast();
+        reset(mProxyTracker);
+
+        // Proxy is removed, send a proxy broadcast.
+        lp.setHttpProxy(null);
+        mMockVpn.sendLinkProperties(lp);
+        waitForIdle();
+        verify(mProxyTracker, times(1)).sendProxyBroadcast();
+        reset(mProxyTracker);
+
+        // Proxy is added in WiFi(default network), setDefaultProxy will be called.
+        final LinkProperties wifiLp = mCm.getLinkProperties(mWiFiNetworkAgent.getNetwork());
+        assertNotNull(wifiLp);
+        wifiLp.setHttpProxy(testProxyInfo);
+        mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+        waitForIdle();
+        verify(mProxyTracker, times(1)).setDefaultProxy(eq(testProxyInfo));
+        reset(mProxyTracker);
+    }
+
+    @Test
+    public void testProxyBroadcastWillBeSentWhenVpnHasProxyAndConnects() throws Exception {
+        // Set up a WiFi network without proxy.
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(true);
+        assertNull(mService.getProxyForNetwork(null));
+        assertNull(mCm.getDefaultProxy());
+
+        final LinkProperties lp = new LinkProperties();
+        lp.setInterfaceName("tun0");
+        lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
+        lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
+        final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
+        lp.setHttpProxy(testProxyInfo);
+        final UidRange vpnRange = PRIMARY_UIDRANGE;
+        final Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
+        mMockVpn.setOwnerAndAdminUid(VPN_UID);
+        mMockVpn.registerAgent(false, vpnRanges, lp);
+        // In any case, the proxy broadcast won't be sent before VPN goes into CONNECTED state.
+        // Otherwise, the app that calls ConnectivityManager#getDefaultProxy() when it receives the
+        // proxy broadcast will get null.
+        verify(mProxyTracker, never()).sendProxyBroadcast();
+        mMockVpn.connect(true /* validated */, true /* hasInternet */, false /* isStrictMode */);
+        waitForIdle();
+        assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
+        // Vpn is connected with proxy, so the proxy broadcast will be sent to inform the apps to
+        // update their proxy data.
+        verify(mProxyTracker, times(1)).sendProxyBroadcast();
+    }
+
+    @Test
+    public void testProxyBroadcastWillBeSentWhenTheProxyOfNonDefaultNetworkHasChanged()
+            throws Exception {
+        // Set up a CELLULAR network without proxy.
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        assertNull(mService.getProxyForNetwork(null));
+        assertNull(mCm.getDefaultProxy());
+        // CELLULAR network should be the default network.
+        assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
+        // Set up a WiFi network without proxy.
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(true);
+        assertNull(mService.getProxyForNetwork(null));
+        assertNull(mCm.getDefaultProxy());
+        // WiFi network should be the default network.
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+        // CELLULAR network is not the default network.
+        assertNotEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
+        // CELLULAR network is not the system default network, but it might be a per-app default
+        // network. The proxy broadcast should be sent once its proxy has changed.
+        final LinkProperties cellularLp = new LinkProperties();
+        cellularLp.setInterfaceName(MOBILE_IFNAME);
+        final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
+        cellularLp.setHttpProxy(testProxyInfo);
+        mCellNetworkAgent.sendLinkProperties(cellularLp);
+        waitForIdle();
+        verify(mProxyTracker, times(1)).sendProxyBroadcast();
     }
 
     @Test
@@ -10588,29 +10662,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);
     }
@@ -10631,8 +10706,7 @@
     }
 
     @Test
-    public void testOemNetworkRequestFactoryPreferenceUninitializedThrowsError()
-            throws PackageManager.NameNotFoundException {
+    public void testOemNetworkRequestFactoryPreferenceUninitializedThrowsError() {
         @OemNetworkPreferences.OemNetworkPreference final int prefToTest =
                 OEM_NETWORK_PREFERENCE_UNINITIALIZED;
 
@@ -10658,8 +10732,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());
@@ -10687,8 +10762,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());
@@ -10713,8 +10789,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());
@@ -10736,8 +10813,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());
@@ -10804,16 +10882,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;
@@ -10827,8 +10907,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());
@@ -10836,11 +10916,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
@@ -10894,7 +10973,48 @@
         assertThrows(UnsupportedOperationException.class,
                 () -> mService.setOemNetworkPreference(
                         createDefaultOemNetworkPreferences(networkPref),
-                        new TestOemListenerCallback()));
+                        null));
+    }
+
+    @Test
+    public void testSetOemNetworkPreferenceFailsForTestRequestWithoutPermission() {
+        // Calling setOemNetworkPreference() with a test pref requires the permission
+        // MANAGE_TEST_NETWORKS.
+        mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, false);
+        @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+                OEM_NETWORK_PREFERENCE_TEST;
+
+        // Act on ConnectivityService.setOemNetworkPreference()
+        assertThrows(SecurityException.class,
+                () -> mService.setOemNetworkPreference(
+                        createDefaultOemNetworkPreferences(networkPref),
+                        null));
+    }
+
+    @Test
+    public void testSetOemNetworkPreferenceFailsForInvalidTestRequest() {
+        assertSetOemNetworkPreferenceFailsForInvalidTestRequest(OEM_NETWORK_PREFERENCE_TEST);
+    }
+
+    @Test
+    public void testSetOemNetworkPreferenceFailsForInvalidTestOnlyRequest() {
+        assertSetOemNetworkPreferenceFailsForInvalidTestRequest(OEM_NETWORK_PREFERENCE_TEST_ONLY);
+    }
+
+    private void assertSetOemNetworkPreferenceFailsForInvalidTestRequest(
+            @OemNetworkPreferences.OemNetworkPreference final int oemNetworkPreferenceForTest) {
+        mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, true);
+        final String secondPackage = "does.not.matter";
+
+        // A valid test request would only have a single mapping.
+        final OemNetworkPreferences pref = new OemNetworkPreferences.Builder()
+                .addNetworkPreference(TEST_PACKAGE_NAME, oemNetworkPreferenceForTest)
+                .addNetworkPreference(secondPackage, oemNetworkPreferenceForTest)
+                .build();
+
+        // Act on ConnectivityService.setOemNetworkPreference()
+        assertThrows(IllegalArgumentException.class,
+                () -> mService.setOemNetworkPreference(pref, null));
     }
 
     private void setOemNetworkPreferenceAgentConnected(final int transportType,
@@ -11001,16 +11121,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);
     }
@@ -11025,6 +11153,9 @@
         if (null != mProfileDefaultNetworkCallback) {
             mCm.unregisterNetworkCallback(mProfileDefaultNetworkCallback);
         }
+        if (null != mTestPackageDefaultNetworkCallback) {
+            mCm.unregisterNetworkCallback(mTestPackageDefaultNetworkCallback);
+        }
     }
 
     private void setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(
@@ -11060,8 +11191,18 @@
     private void setupSetOemNetworkPreferenceForPreferenceTest(
             @OemNetworkPreferences.OemNetworkPreference final int networkPrefToSetup,
             @NonNull final UidRangeParcel[] uidRanges,
-            @NonNull final String testPackageName)
-            throws Exception {
+            @NonNull final String testPackageName) throws Exception {
+        setupSetOemNetworkPreferenceForPreferenceTest(
+                networkPrefToSetup, uidRanges, testPackageName, true);
+    }
+
+    private void setupSetOemNetworkPreferenceForPreferenceTest(
+            @OemNetworkPreferences.OemNetworkPreference final int networkPrefToSetup,
+            @NonNull final UidRangeParcel[] uidRanges,
+            @NonNull final String testPackageName,
+            final boolean hasAutomotiveFeature) throws Exception {
+        mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, hasAutomotiveFeature);
+
         // These tests work off a single UID therefore using 'start' is valid.
         mockGetApplicationInfo(testPackageName, uidRanges[0].start);
 
@@ -11366,6 +11507,55 @@
     }
 
     /**
+     * Test the tracked default requests allows test requests without standard setup.
+     */
+    @Test
+    public void testSetOemNetworkPreferenceAllowsValidTestRequestWithoutChecks() throws Exception {
+        @OemNetworkPreferences.OemNetworkPreference int networkPref =
+                OEM_NETWORK_PREFERENCE_TEST;
+        validateSetOemNetworkPreferenceAllowsValidTestPrefRequest(networkPref);
+    }
+
+    /**
+     * Test the tracked default requests allows test only requests without standard setup.
+     */
+    @Test
+    public void testSetOemNetworkPreferenceAllowsValidTestOnlyRequestWithoutChecks()
+            throws Exception {
+        @OemNetworkPreferences.OemNetworkPreference int networkPref =
+                OEM_NETWORK_PREFERENCE_TEST_ONLY;
+        validateSetOemNetworkPreferenceAllowsValidTestPrefRequest(networkPref);
+    }
+
+    private void validateSetOemNetworkPreferenceAllowsValidTestPrefRequest(int networkPref)
+            throws Exception {
+        // The caller must have the MANAGE_TEST_NETWORKS permission.
+        final int testPackageUid = 123;
+        final String validTestPackageName = "does.not.matter";
+        final UidRangeParcel[] uidRanges =
+                toUidRangeStableParcels(uidRangesForUids(testPackageUid));
+        mServiceContext.setPermission(
+                Manifest.permission.MANAGE_TEST_NETWORKS, PERMISSION_GRANTED);
+
+        // Put the system into a state in which setOemNetworkPreference() would normally fail. This
+        // will confirm that a valid test request can bypass these checks.
+        mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, false);
+        mServiceContext.setPermission(
+                Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE, PERMISSION_DENIED);
+
+        // Validate the starting requests only includes the system default request.
+        assertEquals(1, mService.mDefaultNetworkRequests.size());
+
+        // Add an OEM default network request to track.
+        setupSetOemNetworkPreferenceForPreferenceTest(
+                networkPref, uidRanges, validTestPackageName,
+                false /* hasAutomotiveFeature */);
+
+        // Two requests should now exist; the system default and the test request.
+        assertEquals(2, mService.mDefaultNetworkRequests.size());
+    }
+
+    /**
      * Test the tracked default requests clear previous OEM requests on setOemNetworkPreference().
      */
     @Test
@@ -11377,7 +11567,7 @@
         final UidRangeParcel[] uidRanges =
                 toUidRangeStableParcels(uidRangesForUids(testPackageUid));
 
-        // Validate the starting requests only includes the fallback request.
+        // Validate the starting requests only includes the system default request.
         assertEquals(1, mService.mDefaultNetworkRequests.size());
 
         // Add an OEM default network request to track.
@@ -11641,6 +11831,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.
@@ -11683,6 +11874,7 @@
         final UidRangeParcel[] uidRangesBothUsers =
                 toUidRangeStableParcels(
                         uidRangesForUids(TEST_PACKAGE_UID, secondUserTestPackageUid));
+        mockGetApplicationInfo(TEST_PACKAGE_NAME, secondUserTestPackageUid, secondUserHandle);
         setupSetOemNetworkPreferenceForPreferenceTest(
                 networkPref, uidRangesSingleUser, TEST_PACKAGE_NAME);
 
@@ -11739,7 +11931,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);
 
@@ -11773,7 +11965,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);
@@ -12808,8 +13000,8 @@
     @Test
     public void testNetworkRequestWithSubIdsWithNetworkFactoryPermission() throws Exception {
         mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_GRANTED);
-        final PendingIntent pendingIntent = wrapPendingIntent(PendingIntent.getBroadcast(
-                mContext, 0 /* requestCode */, new Intent("a"), FLAG_IMMUTABLE));
+        final PendingIntent pendingIntent = PendingIntent.getBroadcast(
+                mContext, 0 /* requestCode */, new Intent("a"), FLAG_IMMUTABLE);
         final NetworkCallback networkCallback1 = new NetworkCallback();
         final NetworkCallback networkCallback2 = new NetworkCallback();
 
@@ -12825,8 +13017,8 @@
     @Test
     public void testNetworkRequestWithSubIdsWithoutNetworkFactoryPermission() throws Exception {
         mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_DENIED);
-        final PendingIntent pendingIntent = wrapPendingIntent(PendingIntent.getBroadcast(
-                mContext, 0 /* requestCode */, new Intent("a"), FLAG_IMMUTABLE));
+        final PendingIntent pendingIntent = PendingIntent.getBroadcast(
+                mContext, 0 /* requestCode */, new Intent("a"), FLAG_IMMUTABLE);
 
         final Class<SecurityException> expected = SecurityException.class;
         assertThrows(
@@ -12914,6 +13106,8 @@
         assertEquals(1, nris.size());
         assertTrue(nri.isMultilayerRequest());
         assertEquals(nri.getUids(), uidRangesForUids(uids));
+        assertEquals(DEFAULT_NETWORK_PRIORITY_MOBILE_DATA_PREFERRED,
+                nri.getDefaultNetworkPriority());
     }
 
     /**
@@ -12946,9 +13140,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(
@@ -12959,12 +13155,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));
@@ -12972,5 +13171,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);
     }
diff --git a/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java b/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
index d6bd08e..38f6d7f 100644
--- a/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
@@ -116,7 +116,6 @@
     private <T> void mockService(String serviceName, Class<T> serviceClass, T service) {
         when(mContext.getSystemServiceName(serviceClass)).thenReturn(serviceName);
         when(mContext.getSystemService(serviceName)).thenReturn(service);
-        when(mContext.getSystemService(serviceClass)).thenReturn(service);
     }
 
     @Before
diff --git a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
index 0bfc129..e98f5db 100644
--- a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -139,7 +139,6 @@
                 .thenReturn(Context.SYSTEM_CONFIG_SERVICE);
         when(mContext.getSystemService(Context.SYSTEM_CONFIG_SERVICE))
                 .thenReturn(mSystemConfigManager);
-        when(mContext.getSystemService(SystemConfigManager.class)).thenReturn(mSystemConfigManager);
         when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]);
         final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mContext));
         doReturn(UserHandle.ALL).when(asUserCtx).getUser();
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index a086451..b725b82 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -219,11 +219,19 @@
 
         when(mContext.getPackageName()).thenReturn(TEST_VPN_PKG);
         when(mContext.getOpPackageName()).thenReturn(TEST_VPN_PKG);
-        mockService(UserManager.class, Context.USER_SERVICE, mUserManager);
-        mockService(AppOpsManager.class, Context.APP_OPS_SERVICE, mAppOps);
-        mockService(NotificationManager.class, Context.NOTIFICATION_SERVICE, mNotificationManager);
-        mockService(ConnectivityManager.class, Context.CONNECTIVITY_SERVICE, mConnectivityManager);
-        mockService(IpSecManager.class, Context.IPSEC_SERVICE, mIpSecManager);
+        when(mContext.getSystemServiceName(UserManager.class))
+                .thenReturn(Context.USER_SERVICE);
+        when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
+        when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps);
+        when(mContext.getSystemServiceName(NotificationManager.class))
+                .thenReturn(Context.NOTIFICATION_SERVICE);
+        when(mContext.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
+                .thenReturn(mNotificationManager);
+        when(mContext.getSystemService(eq(Context.CONNECTIVITY_SERVICE)))
+                .thenReturn(mConnectivityManager);
+        when(mContext.getSystemServiceName(eq(ConnectivityManager.class)))
+                .thenReturn(Context.CONNECTIVITY_SERVICE);
+        when(mContext.getSystemService(eq(Context.IPSEC_SERVICE))).thenReturn(mIpSecManager);
         when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent))
                 .thenReturn(Resources.getSystem().getString(
                         R.string.config_customVpnAlwaysOnDisconnectedDialogComponent));
@@ -251,12 +259,6 @@
                 .thenReturn(tunnelResp);
     }
 
-    private <T> void mockService(Class<T> clazz, String name, T service) {
-        doReturn(service).when(mContext).getSystemService(clazz);
-        doReturn(service).when(mContext).getSystemService(name);
-        doReturn(name).when(mContext).getSystemServiceName(clazz);
-    }
-
     private Set<Range<Integer>> rangeSet(Range<Integer> ... ranges) {
         final Set<Range<Integer>> range = new ArraySet<>();
         for (Range<Integer> r : ranges) range.add(r);
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index da2fdf4..0ba5f7d 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -16,16 +16,12 @@
 
 package com.android.server.net;
 
-import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
-import static android.Manifest.permission.UPDATE_DEVICE_STATS;
 import static android.content.Intent.ACTION_UID_REMOVED;
 import static android.content.Intent.EXTRA_UID;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.NetworkIdentity.OEM_PAID;
 import static android.net.NetworkIdentity.OEM_PRIVATE;
-import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
 import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
 import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
 import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
@@ -110,7 +106,6 @@
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
 
-import androidx.annotation.Nullable;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -204,26 +199,6 @@
             if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager;
             return mBaseContext.getSystemService(name);
         }
-
-        @Override
-        public void enforceCallingOrSelfPermission(String permission, @Nullable String message) {
-            if (checkCallingOrSelfPermission(permission) != PERMISSION_GRANTED) {
-                super.enforceCallingOrSelfPermission(permission, message);
-            }
-        }
-
-        @Override
-        public int checkCallingOrSelfPermission(String permission) {
-            switch (permission) {
-                case PERMISSION_MAINLINE_NETWORK_STACK:
-                case READ_NETWORK_USAGE_HISTORY:
-                case UPDATE_DEVICE_STATS:
-                    return PERMISSION_GRANTED;
-                default:
-                    return super.checkCallingOrSelfPermission(permission);
-            }
-
-        }
     }
 
     private final Clock mClock = new SimpleClock(ZoneOffset.UTC) {