Merge "Add a path for GNSS time suggestions"
diff --git a/core/api/current.txt b/core/api/current.txt
index 34ccaa1..1ef98be 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -29588,6 +29588,7 @@
     field public static final String ID;
     field public static final String MANUFACTURER;
     field public static final String MODEL;
+    field @NonNull public static final String ODM_SKU;
     field public static final String PRODUCT;
     field @Deprecated public static final String RADIO;
     field @Deprecated public static final String SERIAL;
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index abdd537..06ad9c9 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -15,22 +15,18 @@
 # Notification
 per-file *Notification* = file:/packages/SystemUI/OWNERS
 
-#Wallpaper
-per-file Wallpaper*.java = file:/core/java/android/service/wallpaper/OWNERS
-per-file IWallpaper*.aidl = file:/core/java/android/service/wallpaper/OWNERS
+# ResourcesManager
+per-file ResourcesManager = rtmitchell@google.com, toddke@google.com
+
+# Wallpaper
+per-file *Wallpaper* = file:/core/java/android/service/wallpaper/OWNERS
 
 # WindowManager
-per-file Activity*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
-per-file Activity*.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file *Activity* = file:/services/core/java/com/android/server/wm/OWNERS
 per-file ClientTransactionHandler.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file Fragment.java = file:/services/core/java/com/android/server/wm/OWNERS
-per-file IActivity*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
-per-file IAppTask.aidl = file:/services/core/java/com/android/server/wm/OWNERS
-per-file ITaskStackListener.aidl = file:/services/core/java/com/android/server/wm/OWNERS
-per-file LocalActivityManager.java = file:/services/core/java/com/android/server/wm/OWNERS
-per-file Task*.java = file:/services/core/java/com/android/server/wm/OWNERS
-per-file Window*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
-per-file Window*.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file *Task* = file:/services/core/java/com/android/server/wm/OWNERS
+per-file Window* = file:/services/core/java/com/android/server/wm/OWNERS
 
 # TODO(b/174932174): determine the ownership of KeyguardManager.java
 
diff --git a/core/java/android/app/admin/OWNERS b/core/java/android/app/admin/OWNERS
index 64a1d27..8462cbe 100644
--- a/core/java/android/app/admin/OWNERS
+++ b/core/java/android/app/admin/OWNERS
@@ -1,4 +1,11 @@
 # Bug component: 142675
 
-yamasani@google.com
+# Android Enterprise team
 rubinxu@google.com
+sandness@google.com
+eranm@google.com
+alexkershaw@google.com
+pgrafov@google.com
+
+# Emeritus
+yamasani@google.com
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 920d34f..a00ff8e 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -1693,35 +1693,24 @@
     private static native long nativeAllocateCopy(long ptr)
             throws NullPointerException;
 
-    @FastNative
     private static synchronized native void nativeWriteToParcel(Parcel dest, long ptr);
-    @FastNative
     private static synchronized native void nativeReadFromParcel(Parcel source, long ptr);
-    @FastNative
     private static synchronized native void nativeSwap(long ptr, long otherPtr)
             throws NullPointerException;
-    @FastNative
     private static synchronized native void nativeClose(long ptr);
-    @FastNative
     private static synchronized native boolean nativeIsEmpty(long ptr);
-    @FastNative
     private static synchronized native int nativeGetEntryCount(long ptr);
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @FastNative
     private static synchronized native byte[] nativeReadValues(int tag, long ptr);
-    @FastNative
     private static synchronized native void nativeWriteValues(int tag, byte[] src, long ptr);
     private static synchronized native void nativeDump(long ptr) throws IOException; // dump to LOGD
 
-    @FastNative
     private static synchronized native ArrayList nativeGetAllVendorKeys(long ptr, Class keyClass);
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @FastNative
     private static synchronized native int nativeGetTagFromKeyLocal(long ptr, String keyName)
             throws IllegalArgumentException;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @FastNative
     private static synchronized native int nativeGetTypeFromTagLocal(long ptr, int tag)
             throws IllegalArgumentException;
     @FastNative
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 78ba7f0..0d8769e 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -106,12 +106,24 @@
     public static final String HARDWARE = getString("ro.hardware");
 
     /**
-     * The hardware variant (SKU), if available.
+     * The SKU of the hardware (from the kernel command line). The SKU is reported by the bootloader
+     * to configure system software features.
      */
     @NonNull
     public static final String SKU = getString("ro.boot.hardware.sku");
 
     /**
+     * The SKU of the device as set by the original design manufacturer (ODM). This is a
+     * runtime-initialized property set during startup to configure device services.
+     *
+     * <p>The ODM SKU may have multiple variants for the same system SKU in case a manufacturer
+     * produces variants of the same design. For example, the same build may be released with
+     * variations in physical keyboard and/or display hardware, each with a different ODM SKU.
+     */
+    @NonNull
+    public static final String ODM_SKU = getString("ro.boot.product.hardware.sku");
+
+    /**
      * Whether this build was for an emulator device.
      * @hide
      */
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index e1d9005..25d84ba 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -321,16 +321,6 @@
     void setFirewallChainEnabled(int chain, boolean enable);
 
     /**
-     * Set all packets from users in ranges to go through VPN specified by netId.
-     */
-    void addVpnUidRanges(int netId, in UidRange[] ranges);
-
-    /**
-     * Clears the special VPN rules for users in ranges and VPN specified by netId.
-     */
-    void removeVpnUidRanges(int netId, in UidRange[] ranges);
-
-    /**
      * Start listening for mobile activity state changes.
      */
     void registerNetworkActivityListener(INetworkActivityListener listener);
@@ -361,7 +351,5 @@
     void removeInterfaceFromLocalNetwork(String iface);
     int removeRoutesFromLocalNetwork(in List<RouteInfo> routes);
 
-    void setAllowOnlyVpnForUids(boolean enable, in UidRange[] uidRanges);
-
     boolean isNetworkRestricted(int uid);
 }
diff --git a/core/java/com/android/internal/view/OWNERS b/core/java/com/android/internal/view/OWNERS
index 99a7686..851d1f3 100644
--- a/core/java/com/android/internal/view/OWNERS
+++ b/core/java/com/android/internal/view/OWNERS
@@ -2,4 +2,19 @@
 
 file:/core/java/android/view/OWNERS
 
-per-file *Input* = file:/services/core/java/com/android/server/inputmethod/OWNERS
\ No newline at end of file
+# Autofill
+per-file IInlineSuggestions*.aidl = file:/core/java/android/service/autofill/OWNERS
+per-file InlineSuggestions*.java = file:/core/java/android/service/autofill/OWNERS
+
+# Ime
+per-file *Input* = file:/services/core/java/com/android/server/inputmethod/OWNERS
+
+# Surface
+per-file *Surface* = file:/graphics/java/android/graphics/OWNERS
+per-file *Surface* = file:/services/core/java/com/android/server/wm/OWNERS
+
+# WindowManager
+per-file AppearanceRegion = file:/services/core/java/com/android/server/wm/OWNERS
+per-file BaseIWIndow.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file RotationPolicy.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file WindowManagerPolicyThread.java = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/tests/coretests/src/android/app/OWNERS b/core/tests/coretests/src/android/app/OWNERS
new file mode 100644
index 0000000..bd7da0c
--- /dev/null
+++ b/core/tests/coretests/src/android/app/OWNERS
@@ -0,0 +1 @@
+per-file Window*.java = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/tests/coretests/src/android/app/activity/OWNERS b/core/tests/coretests/src/android/app/activity/OWNERS
new file mode 100644
index 0000000..0862c05
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/core/tests/coretests/src/android/app/servertransaction/OWNERS b/core/tests/coretests/src/android/app/servertransaction/OWNERS
new file mode 100644
index 0000000..0862c05
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/core/tests/coretests/src/android/content/OWNERS b/core/tests/coretests/src/android/content/OWNERS
new file mode 100644
index 0000000..911efb2
--- /dev/null
+++ b/core/tests/coretests/src/android/content/OWNERS
@@ -0,0 +1 @@
+per-file ContextTest.java = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/tests/mockingcoretests/src/android/app/activity/OWNERS b/core/tests/mockingcoretests/src/android/app/activity/OWNERS
new file mode 100644
index 0000000..0862c05
--- /dev/null
+++ b/core/tests/mockingcoretests/src/android/app/activity/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index c2168f1..e7e83eb 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -25,6 +25,7 @@
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
+import android.os.Process;
 import android.os.SystemProperties;
 import android.util.Log;
 import android.util.Pair;
@@ -188,13 +189,14 @@
 
     // COMMON CONSTANTS
     private static final Range<Integer> POSITIVE_INTEGERS =
-        Range.create(1, Integer.MAX_VALUE);
+            Range.create(1, Integer.MAX_VALUE);
     private static final Range<Long> POSITIVE_LONGS =
-        Range.create(1l, Long.MAX_VALUE);
+            Range.create(1L, Long.MAX_VALUE);
     private static final Range<Rational> POSITIVE_RATIONALS =
-        Range.create(new Rational(1, Integer.MAX_VALUE),
-                     new Rational(Integer.MAX_VALUE, 1));
-    private static final Range<Integer> SIZE_RANGE = Range.create(1, 32768);
+            Range.create(new Rational(1, Integer.MAX_VALUE),
+                         new Rational(Integer.MAX_VALUE, 1));
+    private static final Range<Integer> SIZE_RANGE =
+            Process.is64Bit() ? Range.create(1, 32768) : Range.create(1, 4096);
     private static final Range<Integer> FRAME_RATE_RANGE = Range.create(0, 960);
     private static final Range<Integer> BITRATE_RANGE = Range.create(0, 500000000);
     private static final int DEFAULT_MAX_SUPPORTED_INSTANCES = 32;
@@ -1399,6 +1401,9 @@
 
         /**
          * Returns the range of supported video widths.
+         * <p class=note>
+         * 32-bit processes will not support resolutions larger than 4096x4096 due to
+         * the limited address space.
          */
         public Range<Integer> getSupportedWidths() {
             return mWidthRange;
@@ -1406,6 +1411,9 @@
 
         /**
          * Returns the range of supported video heights.
+         * <p class=note>
+         * 32-bit processes will not support resolutions larger than 4096x4096 due to
+         * the limited address space.
          */
         public Range<Integer> getSupportedHeights() {
             return mHeightRange;
@@ -1857,6 +1865,10 @@
                         && aligned.mMaxMacroBlockRate >= otherAligned.mMaxMacroBlockRate);
             }
 
+            /* package private */ boolean isEqualDimension(@NonNull PerformancePoint other) {
+                return mWidth == other.mWidth && mHeight == other.mHeight;
+            }
+
             private @NonNull Size getCommonBlockSize(@NonNull PerformancePoint other) {
                 return new Size(
                         Math.max(mBlockSize.getWidth(), other.mBlockSize.getWidth()) * 16,
@@ -1997,6 +2009,9 @@
          * Performance points assume a single active codec. For use cases where multiple
          * codecs are active, should use that highest pixel count, and add the frame rates of
          * each individual codec.
+         * <p class=note>
+         * Supported resolution could be further restricted for 32-bit processes due to
+         * the limited virtual memory space.
          */
         @Nullable
         public List<PerformancePoint> getSupportedPerformancePoints() {
@@ -2164,6 +2179,12 @@
                 if (size == null || size.getWidth() * size.getHeight() <= 0) {
                     continue;
                 }
+                if (size.getWidth() > SIZE_RANGE.getUpper()
+                        || size.getHeight() > SIZE_RANGE.getUpper()) {
+                    size = new Size(
+                            Math.min(size.getWidth(), SIZE_RANGE.getUpper()),
+                            Math.min(size.getHeight(), SIZE_RANGE.getUpper()));
+                }
                 Range<Long> range = Utils.parseLongRange(map.get(key), null);
                 if (range == null || range.getLower() < 0 || range.getUpper() < 0) {
                     continue;
@@ -2193,6 +2214,29 @@
                                (a.getMaxMacroBlockRate() < b.getMaxMacroBlockRate() ? -1 : 1) :
                        (a.getMaxFrameRate() != b.getMaxFrameRate()) ?
                                (a.getMaxFrameRate() < b.getMaxFrameRate() ? -1 : 1) : 0));
+
+            // remove redundant points
+            for (int i = 1; i < ret.size(); ++i) {
+                PerformancePoint a = ret.get(i);
+                for (int j = 0; j < i; ++j) {
+                    PerformancePoint b = ret.get(j);
+                    if (b.isEqualDimension(a) && b.covers(a)) {
+                        ret.set(i, null);
+                        break;
+                    }
+                }
+            }
+            int newSize = 0;
+            for (int i = 0; i < ret.size(); ++i) {
+                PerformancePoint a = ret.get(i);
+                if (a == null) {
+                    continue;
+                }
+                ret.set(newSize, a);
+                ++newSize;
+            }
+            ret.setSize(newSize);
+
             return Collections.unmodifiableList(ret);
         }
 
diff --git a/packages/Connectivity/OWNERS b/packages/Connectivity/OWNERS
new file mode 100644
index 0000000..48e54da
--- /dev/null
+++ b/packages/Connectivity/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+include platform/frameworks/base:/services/core/java/com/android/server/net/OWNERS
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d867442..4590aa2 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -129,6 +129,7 @@
 import android.net.SocketKeepalive;
 import android.net.TetheringManager;
 import android.net.UidRange;
+import android.net.UidRangeParcel;
 import android.net.Uri;
 import android.net.VpnManager;
 import android.net.VpnService;
@@ -2822,6 +2823,7 @@
                     break;
                 }
                 case NetworkAgent.EVENT_UNDERLYING_NETWORKS_CHANGED: {
+                    // TODO: prevent loops, e.g., if a network declares itself as underlying.
                     if (!nai.supportsUnderlyingNetworks()) {
                         Log.wtf(TAG, "Non-virtual networks cannot have underlying networks");
                         break;
@@ -3421,6 +3423,7 @@
             }
         }
         nai.clearLingerState();
+        propagateUnderlyingNetworkCapabilities(nai.network);
         if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
             mDefaultNetworkNai = null;
             updateDataActivityTracking(null /* newNetwork */, nai);
@@ -3428,9 +3431,6 @@
             ensureNetworkTransitionWakelock(nai.toShortString());
         }
         mLegacyTypeTracker.remove(nai, wasDefault);
-        if (!nai.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
-            propagateUnderlyingNetworkCapabilities();
-        }
         rematchAllNetworksAndRequests();
         mLingerMonitor.noteDisconnect(nai);
         if (nai.created) {
@@ -4819,17 +4819,35 @@
         }
     }
 
+    private Network[] underlyingNetworksOrDefault(Network[] underlyingNetworks) {
+        final Network defaultNetwork = getNetwork(getDefaultNetwork());
+        if (underlyingNetworks == null && defaultNetwork != null) {
+            // null underlying networks means to track the default.
+            underlyingNetworks = new Network[] { defaultNetwork };
+        }
+        return underlyingNetworks;
+    }
+
+    // Returns true iff |network| is an underlying network of |nai|.
+    private boolean hasUnderlyingNetwork(NetworkAgentInfo nai, Network network) {
+        // TODO: support more than one level of underlying networks, either via a fixed-depth search
+        // (e.g., 2 levels of underlying networks), or via loop detection, or....
+        if (!nai.supportsUnderlyingNetworks()) return false;
+        final Network[] underlying = underlyingNetworksOrDefault(nai.declaredUnderlyingNetworks);
+        return ArrayUtils.contains(underlying, network);
+    }
+
     /**
-     * Ask all networks with underlying networks to recompute and update their capabilities.
+     * Recompute the capabilities for any networks that had a specific network as underlying.
      *
      * When underlying networks change, such networks may have to update capabilities to reflect
      * things like the metered bit, their transports, and so on. The capabilities are calculated
      * immediately. This method runs on the ConnectivityService thread.
      */
-    private void propagateUnderlyingNetworkCapabilities() {
+    private void propagateUnderlyingNetworkCapabilities(Network updatedNetwork) {
         ensureRunningOnConnectivityServiceThread();
         for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
-            if (nai.supportsUnderlyingNetworks()) {
+            if (updatedNetwork == null || hasUnderlyingNetwork(nai, updatedNetwork)) {
                 updateCapabilitiesForNetwork(nai);
             }
         }
@@ -5152,7 +5170,7 @@
                 loge("Starting user already has a VPN");
                 return;
             }
-            userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, userId, mKeyStore);
+            userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, mNetd, userId, mKeyStore);
             mVpns.put(userId, userVpn);
             if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
                 updateLockdownVpn();
@@ -6368,27 +6386,28 @@
      * This method should never alter the agent's NetworkCapabilities, only store data in |nai|.
      */
     private void processCapabilitiesFromAgent(NetworkAgentInfo nai, NetworkCapabilities nc) {
-        nai.declaredMetered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED);
+        // Note: resetting the owner UID before storing the agent capabilities in NAI means that if
+        // the agent attempts to change the owner UID, then nai.declaredCapabilities will not
+        // actually be the same as the capabilities sent by the agent. Still, it is safer to reset
+        // the owner UID here and behave as if the agent had never tried to change it.
         if (nai.networkCapabilities.getOwnerUid() != nc.getOwnerUid()) {
             Log.e(TAG, nai.toShortString() + ": ignoring attempt to change owner from "
                     + nai.networkCapabilities.getOwnerUid() + " to " + nc.getOwnerUid());
             nc.setOwnerUid(nai.networkCapabilities.getOwnerUid());
         }
+        nai.declaredCapabilities = new NetworkCapabilities(nc);
     }
 
-    /** Modifies |caps| based on the capabilities of the specified underlying networks. */
+    /** Modifies |newNc| based on the capabilities of |underlyingNetworks| and |agentCaps|. */
     @VisibleForTesting
     void applyUnderlyingCapabilities(@Nullable Network[] underlyingNetworks,
-            @NonNull NetworkCapabilities caps,  boolean declaredMetered) {
-        final Network defaultNetwork = getNetwork(getDefaultNetwork());
-        if (underlyingNetworks == null && defaultNetwork != null) {
-            // null underlying networks means to track the default.
-            underlyingNetworks = new Network[] { defaultNetwork };
-        }
-        int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN };
+            @NonNull NetworkCapabilities agentCaps, @NonNull NetworkCapabilities newNc) {
+        underlyingNetworks = underlyingNetworksOrDefault(underlyingNetworks);
+        int[] transportTypes = agentCaps.getTransportTypes();
         int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
         int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
-        boolean metered = declaredMetered; // metered if any underlying is metered, or agentMetered
+        // metered if any underlying is metered, or originally declared metered by the agent.
+        boolean metered = !agentCaps.hasCapability(NET_CAPABILITY_NOT_METERED);
         boolean roaming = false; // roaming if any underlying is roaming
         boolean congested = false; // congested if any underlying is congested
         boolean suspended = true; // suspended if all underlying are suspended
@@ -6434,13 +6453,13 @@
             suspended = false;
         }
 
-        caps.setTransportTypes(transportTypes);
-        caps.setLinkDownstreamBandwidthKbps(downKbps);
-        caps.setLinkUpstreamBandwidthKbps(upKbps);
-        caps.setCapability(NET_CAPABILITY_NOT_METERED, !metered);
-        caps.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming);
-        caps.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested);
-        caps.setCapability(NET_CAPABILITY_NOT_SUSPENDED, !suspended);
+        newNc.setTransportTypes(transportTypes);
+        newNc.setLinkDownstreamBandwidthKbps(downKbps);
+        newNc.setLinkUpstreamBandwidthKbps(upKbps);
+        newNc.setCapability(NET_CAPABILITY_NOT_METERED, !metered);
+        newNc.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming);
+        newNc.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested);
+        newNc.setCapability(NET_CAPABILITY_NOT_SUSPENDED, !suspended);
     }
 
     /**
@@ -6497,7 +6516,8 @@
         }
 
         if (nai.supportsUnderlyingNetworks()) {
-            applyUnderlyingCapabilities(nai.declaredUnderlyingNetworks, newNc, nai.declaredMetered);
+            applyUnderlyingCapabilities(nai.declaredUnderlyingNetworks, nai.declaredCapabilities,
+                    newNc);
         }
 
         return newNc;
@@ -6576,11 +6596,8 @@
             }
         }
 
-        if (!newNc.hasTransport(TRANSPORT_VPN)) {
-            // Tell VPNs about updated capabilities, since they may need to
-            // bubble those changes through.
-            propagateUnderlyingNetworkCapabilities();
-        }
+        // This network might have been underlying another network. Propagate its capabilities.
+        propagateUnderlyingNetworkCapabilities(nai.network);
 
         if (!newNc.equalsTransportTypes(prevNc)) {
             mDnsManager.updateTransportsForNetwork(
@@ -6622,6 +6639,16 @@
                 && (lp.hasIpv6DefaultRoute() || lp.hasIpv6UnreachableDefaultRoute());
     }
 
+    private static UidRangeParcel[] toUidRangeStableParcels(final @NonNull Set<UidRange> ranges) {
+        final UidRangeParcel[] stableRanges = new UidRangeParcel[ranges.size()];
+        int index = 0;
+        for (UidRange range : ranges) {
+            stableRanges[index] = new UidRangeParcel(range.start, range.stop);
+            index++;
+        }
+        return stableRanges;
+    }
+
     private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc,
             NetworkCapabilities newNc) {
         Set<UidRange> prevRanges = null == prevNc ? null : prevNc.getUids();
@@ -6641,14 +6668,11 @@
             // removing old range works because, unlike the filtering rules below, it's possible to
             // add duplicate UID routing rules.
             if (!newRanges.isEmpty()) {
-                final UidRange[] addedRangesArray = new UidRange[newRanges.size()];
-                newRanges.toArray(addedRangesArray);
-                mNMS.addVpnUidRanges(nai.network.getNetId(), addedRangesArray);
+                mNetd.networkAddUidRanges(nai.network.netId, toUidRangeStableParcels(newRanges));
             }
             if (!prevRanges.isEmpty()) {
-                final UidRange[] removedRangesArray = new UidRange[prevRanges.size()];
-                prevRanges.toArray(removedRangesArray);
-                mNMS.removeVpnUidRanges(nai.network.getNetId(), removedRangesArray);
+                mNetd.networkRemoveUidRanges(
+                        nai.network.netId, toUidRangeStableParcels(prevRanges));
             }
             final boolean wasFiltering = requiresVpnIsolation(nai, prevNc, nai.linkProperties);
             final boolean shouldFilter = requiresVpnIsolation(nai, newNc, nai.linkProperties);
@@ -6903,8 +6927,10 @@
         updateTcpBufferSizes(null != newNetwork
                 ? newNetwork.linkProperties.getTcpBufferSizes() : null);
         notifyIfacesChangedForNetworkStats();
-        // Fix up the NetworkCapabilities of any VPNs that don't specify underlying networks.
-        propagateUnderlyingNetworkCapabilities();
+        // Fix up the NetworkCapabilities of any networks that have this network as underlying.
+        if (newNetwork != null) {
+            propagateUnderlyingNetworkCapabilities(newNetwork.network);
+        }
     }
 
     private void processListenRequests(@NonNull final NetworkAgentInfo nai) {
@@ -7360,13 +7386,11 @@
             networkAgent.networkCapabilities.addCapability(NET_CAPABILITY_FOREGROUND);
 
             if (!createNativeNetwork(networkAgent)) return;
-            if (networkAgent.isVPN()) {
-                // Initialize the VPN capabilities to their starting values according to the
-                // underlying networks. This will avoid a spurious callback to
-                // onCapabilitiesUpdated being sent in updateAllVpnCapabilities below as
-                // the VPN would switch from its default, blank capabilities to those
-                // that reflect the capabilities of its underlying networks.
-                propagateUnderlyingNetworkCapabilities();
+            if (networkAgent.supportsUnderlyingNetworks()) {
+                // Initialize the network's capabilities to their starting values according to the
+                // underlying networks. This ensures that the capabilities are correct before
+                // anything happens to the network.
+                updateCapabilitiesForNetwork(networkAgent);
             }
             networkAgent.created = true;
         }
@@ -7408,10 +7432,6 @@
             // doing.
             updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
 
-            if (networkAgent.supportsUnderlyingNetworks()) {
-                propagateUnderlyingNetworkCapabilities();
-            }
-
             // Consider network even though it is not yet validated.
             rematchAllNetworksAndRequests();
 
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 5e86f85..086cc1c 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -60,7 +60,6 @@
 import android.net.NetworkStats;
 import android.net.RouteInfo;
 import android.net.TetherStatsParcel;
-import android.net.UidRange;
 import android.net.UidRangeParcel;
 import android.net.shared.NetdUtils;
 import android.net.shared.RouteUtils;
@@ -1393,38 +1392,6 @@
         }
     }
 
-    private static UidRangeParcel makeUidRangeParcel(int start, int stop) {
-        UidRangeParcel range = new UidRangeParcel();
-        range.start = start;
-        range.stop = stop;
-        return range;
-    }
-
-    private static UidRangeParcel[] toStableParcels(UidRange[] ranges) {
-        UidRangeParcel[] stableRanges = new UidRangeParcel[ranges.length];
-        for (int i = 0; i < ranges.length; i++) {
-            stableRanges[i] = makeUidRangeParcel(ranges[i].start, ranges[i].stop);
-        }
-        return stableRanges;
-    }
-
-    @Override
-    public void setAllowOnlyVpnForUids(boolean add, UidRange[] uidRanges)
-            throws ServiceSpecificException {
-        NetworkStack.checkNetworkStackPermission(mContext);
-        try {
-            mNetdService.networkRejectNonSecureVpn(add, toStableParcels(uidRanges));
-        } catch (ServiceSpecificException e) {
-            Log.w(TAG, "setAllowOnlyVpnForUids(" + add + ", " + Arrays.toString(uidRanges) + ")"
-                    + ": netd command failed", e);
-            throw e;
-        } catch (RemoteException e) {
-            Log.w(TAG, "setAllowOnlyVpnForUids(" + add + ", " + Arrays.toString(uidRanges) + ")"
-                    + ": netd command failed", e);
-            throw e.rethrowAsRuntimeException();
-        }
-    }
-
     private void applyUidCleartextNetworkPolicy(int uid, int policy) {
         final int policyValue;
         switch (policy) {
@@ -1553,27 +1520,6 @@
     }
 
     @Override
-    public void addVpnUidRanges(int netId, UidRange[] ranges) {
-        NetworkStack.checkNetworkStackPermission(mContext);
-
-        try {
-            mNetdService.networkAddUidRanges(netId, toStableParcels(ranges));
-        } catch (RemoteException | ServiceSpecificException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    @Override
-    public void removeVpnUidRanges(int netId, UidRange[] ranges) {
-        NetworkStack.checkNetworkStackPermission(mContext);
-        try {
-            mNetdService.networkRemoveUidRanges(netId, toStableParcels(ranges));
-        } catch (RemoteException | ServiceSpecificException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    @Override
     public void setFirewallEnabled(boolean enabled) {
         enforceSystemUid();
         try {
@@ -1616,7 +1562,7 @@
             ranges = new UidRangeParcel[] {
                 // TODO: is there a better way of finding all existing users? If so, we could
                 // specify their ranges here.
-                makeUidRangeParcel(Process.FIRST_APPLICATION_UID, Integer.MAX_VALUE),
+                new UidRangeParcel(Process.FIRST_APPLICATION_UID, Integer.MAX_VALUE),
             };
             // ... except for the UIDs that have allow rules.
             synchronized (mRulesLock) {
@@ -1647,7 +1593,7 @@
                 for (int i = 0; i < ranges.length; i++) {
                     if (rules.valueAt(i) == FIREWALL_RULE_DENY) {
                         int uid = rules.keyAt(i);
-                        ranges[numUids] = makeUidRangeParcel(uid, uid);
+                        ranges[numUids] = new UidRangeParcel(uid, uid);
                         numUids++;
                     }
                 }
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index ff0596c..dd497a6 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -4612,14 +4612,7 @@
 
                     // Create package obb and data dir if it doesn't exist.
                     int appUid = UserHandle.getUid(userId, mPmInternal.getPackage(pkg).getUid());
-                    File file = new File(packageObbDir);
-                    if (!file.exists()) {
-                        vold.setupAppDir(packageObbDir, appUid);
-                    }
-                    file = new File(packageDataDir);
-                    if (!file.exists()) {
-                        vold.setupAppDir(packageDataDir, appUid);
-                    }
+                    vold.ensureAppDirsCreated(new String[] {packageObbDir, packageDataDir}, appUid);
                 }
             } catch (ServiceManager.ServiceNotFoundException | RemoteException e) {
                 Slog.e(TAG, "Unable to create obb and data directories for " + processName,e);
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index ccd1f3b..52b9f5c 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -138,9 +138,10 @@
     // not guaranteed to be current or correct, or even to exist.
     public @Nullable Network[] declaredUnderlyingNetworks;
 
-    // Whether this network is always metered even if its underlying networks are unmetered.
-    // Only relevant if #supportsUnderlyingNetworks is true.
-    public boolean declaredMetered;
+    // The capabilities originally announced by the NetworkAgent, regardless of any capabilities
+    // that were added or removed due to this network's underlying networks.
+    // Only set if #supportsUnderlyingNetworks is true.
+    public @Nullable NetworkCapabilities declaredCapabilities;
 
     // Indicates if netd has been told to create this Network. From this point on the appropriate
     // routing rules are setup and routes are added so packets can begin flowing over the Network.
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 66bb4d7..cabfbc0 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -48,6 +48,7 @@
 import android.content.pm.UserInfo;
 import android.net.ConnectivityManager;
 import android.net.DnsResolver;
+import android.net.INetd;
 import android.net.INetworkManagementEventObserver;
 import android.net.Ikev2VpnProfile;
 import android.net.IpPrefix;
@@ -68,6 +69,7 @@
 import android.net.NetworkRequest;
 import android.net.RouteInfo;
 import android.net.UidRange;
+import android.net.UidRangeParcel;
 import android.net.VpnManager;
 import android.net.VpnService;
 import android.net.ipsec.ike.ChildSessionCallback;
@@ -188,7 +190,8 @@
 
     private PendingIntent mStatusIntent;
     private volatile boolean mEnableTeardown = true;
-    private final INetworkManagementService mNetd;
+    private final INetworkManagementService mNms;
+    private final INetd mNetd;
     @VisibleForTesting
     protected VpnConfig mConfig;
     private final NetworkProvider mNetworkProvider;
@@ -234,7 +237,7 @@
      * @see mLockdown
      */
     @GuardedBy("this")
-    private final Set<UidRange> mBlockedUidsAsToldToNetd = new ArraySet<>();
+    private final Set<UidRangeParcel> mBlockedUidsAsToldToNetd = new ArraySet<>();
 
     // The user id of initiating VPN.
     private final int mUserId;
@@ -363,22 +366,23 @@
         }
     }
 
-    public Vpn(Looper looper, Context context, INetworkManagementService netService,
+    public Vpn(Looper looper, Context context, INetworkManagementService netService, INetd netd,
             @UserIdInt int userId, @NonNull KeyStore keyStore) {
-        this(looper, context, new Dependencies(), netService, userId, keyStore,
+        this(looper, context, new Dependencies(), netService, netd, userId, keyStore,
                 new SystemServices(context), new Ikev2SessionCreator());
     }
 
     @VisibleForTesting
     protected Vpn(Looper looper, Context context, Dependencies deps,
-            INetworkManagementService netService,
+            INetworkManagementService netService, INetd netd,
             int userId, @NonNull KeyStore keyStore, SystemServices systemServices,
             Ikev2SessionCreator ikev2SessionCreator) {
         mContext = context;
         mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
         mUserIdContext = context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
         mDeps = deps;
-        mNetd = netService;
+        mNms = netService;
+        mNetd = netd;
         mUserId = userId;
         mLooper = looper;
         mSystemServices = systemServices;
@@ -912,7 +916,7 @@
             }
 
             try {
-                mNetd.denyProtect(mOwnerUID);
+                mNms.denyProtect(mOwnerUID);
             } catch (Exception e) {
                 Log.wtf(TAG, "Failed to disallow UID " + mOwnerUID + " to call protect() " + e);
             }
@@ -922,7 +926,7 @@
             mOwnerUID = getAppUid(newPackage, mUserId);
             mIsPackageTargetingAtLeastQ = doesPackageTargetAtLeastQ(newPackage);
             try {
-                mNetd.allowProtect(mOwnerUID);
+                mNms.allowProtect(mOwnerUID);
             } catch (Exception e) {
                 Log.wtf(TAG, "Failed to allow UID " + mOwnerUID + " to call protect() " + e);
             }
@@ -1579,24 +1583,25 @@
             exemptedPackages = new ArrayList<>(mLockdownAllowlist);
             exemptedPackages.add(mPackage);
         }
-        final Set<UidRange> rangesToTellNetdToRemove = new ArraySet<>(mBlockedUidsAsToldToNetd);
+        final Set<UidRangeParcel> rangesToTellNetdToRemove =
+                new ArraySet<>(mBlockedUidsAsToldToNetd);
 
-        final Set<UidRange> rangesToTellNetdToAdd;
+        final Set<UidRangeParcel> rangesToTellNetdToAdd;
         if (enforce) {
-            final Set<UidRange> rangesThatShouldBeBlocked =
+            final Set<UidRange> restrictedProfilesRanges =
                     createUserAndRestrictedProfilesRanges(mUserId,
-                            /* allowedApplications */ null,
-                            /* disallowedApplications */ exemptedPackages);
+                    /* allowedApplications */ null,
+                    /* disallowedApplications */ exemptedPackages);
+            final Set<UidRangeParcel> rangesThatShouldBeBlocked = new ArraySet<>();
 
             // The UID range of the first user (0-99999) would block the IPSec traffic, which comes
             // directly from the kernel and is marked as uid=0. So we adjust the range to allow
             // it through (b/69873852).
-            for (UidRange range : rangesThatShouldBeBlocked) {
-                if (range.start == 0) {
-                    rangesThatShouldBeBlocked.remove(range);
-                    if (range.stop != 0) {
-                        rangesThatShouldBeBlocked.add(new UidRange(1, range.stop));
-                    }
+            for (UidRange range : restrictedProfilesRanges) {
+                if (range.start == 0 && range.stop != 0) {
+                    rangesThatShouldBeBlocked.add(new UidRangeParcel(1, range.stop));
+                } else if (range.start != 0) {
+                    rangesThatShouldBeBlocked.add(new UidRangeParcel(range.start, range.stop));
                 }
             }
 
@@ -1628,13 +1633,13 @@
      *         including added ranges that already existed or removed ones that didn't.
      */
     @GuardedBy("this")
-    private boolean setAllowOnlyVpnForUids(boolean enforce, Collection<UidRange> ranges) {
+    private boolean setAllowOnlyVpnForUids(boolean enforce, Collection<UidRangeParcel> ranges) {
         if (ranges.size() == 0) {
             return true;
         }
-        final UidRange[] rangesArray = ranges.toArray(new UidRange[ranges.size()]);
+        final UidRangeParcel[] stableRanges = ranges.toArray(new UidRangeParcel[ranges.size()]);
         try {
-            mNetd.setAllowOnlyVpnForUids(enforce, rangesArray);
+            mNetd.networkRejectNonSecureVpn(enforce, stableRanges);
         } catch (RemoteException | RuntimeException e) {
             Log.e(TAG, "Updating blocked=" + enforce
                     + " for UIDs " + Arrays.toString(ranges.toArray()) + " failed", e);
@@ -1849,10 +1854,20 @@
         if (mNetworkInfo.isConnected()) {
             return !appliesToUid(uid);
         } else {
-            return UidRange.containsUid(mBlockedUidsAsToldToNetd, uid);
+            return containsUid(mBlockedUidsAsToldToNetd, uid);
         }
     }
 
+    private boolean containsUid(Collection<UidRangeParcel> ranges, int uid) {
+        if (ranges == null) return false;
+        for (UidRangeParcel range : ranges) {
+            if (range.start <= uid && uid <= range.stop) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void updateAlwaysOnNotification(DetailedState networkState) {
         final boolean visible = (mAlwaysOn && networkState != DetailedState.CONNECTED);
 
@@ -2495,7 +2510,7 @@
                                     address /* unused */,
                                     address /* unused */,
                                     network);
-                    mNetd.setInterfaceUp(mTunnelIface.getInterfaceName());
+                    mNms.setInterfaceUp(mTunnelIface.getInterfaceName());
 
                     mSession = mIkev2SessionCreator.createIkeSession(
                             mContext,
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 0b3cdae..7afa81a 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -165,11 +165,13 @@
 
     private void buildBluetoothRoutes() {
         mBluetoothRoutes.clear();
-        for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) {
-            if (device.isConnected()) {
-                BluetoothRouteInfo newBtRoute = createBluetoothRoute(device);
-                if (newBtRoute.connectedProfiles.size() > 0) {
-                    mBluetoothRoutes.put(device.getAddress(), newBtRoute);
+        if (mBluetoothAdapter.getBondedDevices() != null) {
+            for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) {
+                if (device.isConnected()) {
+                    BluetoothRouteInfo newBtRoute = createBluetoothRoute(device);
+                    if (newBtRoute.connectedProfiles.size() > 0) {
+                        mBluetoothRoutes.put(device.getAddress(), newBtRoute);
+                    }
                 }
             }
         }
diff --git a/test-mock/src/android/test/mock/OWNERS b/test-mock/src/android/test/mock/OWNERS
new file mode 100644
index 0000000..36d9cb2
--- /dev/null
+++ b/test-mock/src/android/test/mock/OWNERS
@@ -0,0 +1 @@
+set noparent
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 9d308c5..8c403f1 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -188,6 +188,7 @@
 import android.net.RouteInfoParcel;
 import android.net.SocketKeepalive;
 import android.net.UidRange;
+import android.net.UidRangeParcel;
 import android.net.Uri;
 import android.net.VpnManager;
 import android.net.metrics.IpConnectivityLog;
@@ -1055,7 +1056,7 @@
 
         public MockVpn(int userId) {
             super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
-                    userId, mock(KeyStore.class));
+                    mMockNetd, userId, mock(KeyStore.class));
             mConfig = new VpnConfig();
         }
 
@@ -1094,10 +1095,11 @@
             mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp,
                     mNetworkCapabilities);
             mMockNetworkAgent.waitForIdle(TIMEOUT_MS);
-            verify(mNetworkManagementService, times(1))
-                    .addVpnUidRanges(eq(mMockVpn.getNetId()), eq(uids.toArray(new UidRange[0])));
-            verify(mNetworkManagementService, never())
-                    .removeVpnUidRanges(eq(mMockVpn.getNetId()), any());
+
+            verify(mMockNetd, times(1)).networkAddUidRanges(eq(mMockVpn.getNetId()),
+                    eq(toUidRangeStableParcels(uids)));
+            verify(mMockNetd, never())
+                    .networkRemoveUidRanges(eq(mMockVpn.getNetId()), any());
             mAgentRegistered = true;
             mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
             mNetworkAgent = mMockNetworkAgent.getNetworkAgent();
@@ -1169,6 +1171,11 @@
         }
     }
 
+    private UidRangeParcel[] toUidRangeStableParcels(final @NonNull Set<UidRange> ranges) {
+        return ranges.stream().map(
+                r -> new UidRangeParcel(r.start, r.stop)).toArray(UidRangeParcel[]::new);
+    }
+
     private void mockVpn(int uid) {
         synchronized (mService.mVpns) {
             int userId = UserHandle.getUserId(uid);
@@ -4947,8 +4954,8 @@
         expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
         reset(mStatsService);
 
-        // Captive portal change shouldn't update ifaces
-        mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
+        // Temp metered change shouldn't update ifaces
+        mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED);
         waitForIdle();
         verify(mStatsService, never())
                 .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
@@ -5464,6 +5471,7 @@
         final Network wifi = mWiFiNetworkAgent.getNetwork();
 
         final NetworkCapabilities initialCaps = new NetworkCapabilities();
+        initialCaps.addTransportType(TRANSPORT_VPN);
         initialCaps.addCapability(NET_CAPABILITY_INTERNET);
         initialCaps.removeCapability(NET_CAPABILITY_NOT_VPN);
 
@@ -5495,44 +5503,45 @@
         withWifiAndMobileUnderlying.setLinkDownstreamBandwidthKbps(10);
         withWifiAndMobileUnderlying.setLinkUpstreamBandwidthKbps(20);
 
+        final NetworkCapabilities initialCapsNotMetered = new NetworkCapabilities(initialCaps);
+        initialCapsNotMetered.addCapability(NET_CAPABILITY_NOT_METERED);
+
         NetworkCapabilities caps = new NetworkCapabilities(initialCaps);
-        final boolean notDeclaredMetered = false;
-        mService.applyUnderlyingCapabilities(new Network[]{}, caps, notDeclaredMetered);
+        mService.applyUnderlyingCapabilities(new Network[]{}, initialCapsNotMetered, caps);
         assertEquals(withNoUnderlying, caps);
 
         caps = new NetworkCapabilities(initialCaps);
-        mService.applyUnderlyingCapabilities(new Network[]{null}, caps, notDeclaredMetered);
+        mService.applyUnderlyingCapabilities(new Network[]{null}, initialCapsNotMetered, caps);
         assertEquals(withNoUnderlying, caps);
 
         caps = new NetworkCapabilities(initialCaps);
-        mService.applyUnderlyingCapabilities(new Network[]{mobile}, caps, notDeclaredMetered);
+        mService.applyUnderlyingCapabilities(new Network[]{mobile}, initialCapsNotMetered, caps);
         assertEquals(withMobileUnderlying, caps);
 
-        mService.applyUnderlyingCapabilities(new Network[]{wifi}, caps, notDeclaredMetered);
+        mService.applyUnderlyingCapabilities(new Network[]{wifi}, initialCapsNotMetered, caps);
         assertEquals(withWifiUnderlying, caps);
 
-        final boolean isDeclaredMetered = true;
         withWifiUnderlying.removeCapability(NET_CAPABILITY_NOT_METERED);
         caps = new NetworkCapabilities(initialCaps);
-        mService.applyUnderlyingCapabilities(new Network[]{wifi}, caps, isDeclaredMetered);
+        mService.applyUnderlyingCapabilities(new Network[]{wifi}, initialCaps, caps);
         assertEquals(withWifiUnderlying, caps);
 
         caps = new NetworkCapabilities(initialCaps);
-        mService.applyUnderlyingCapabilities(new Network[]{mobile, wifi}, caps, isDeclaredMetered);
+        mService.applyUnderlyingCapabilities(new Network[]{mobile, wifi}, initialCaps, caps);
         assertEquals(withWifiAndMobileUnderlying, caps);
 
         withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED);
         caps = new NetworkCapabilities(initialCaps);
         mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi},
-                caps, notDeclaredMetered);
+                initialCapsNotMetered, caps);
         assertEquals(withWifiAndMobileUnderlying, caps);
 
         caps = new NetworkCapabilities(initialCaps);
         mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi},
-                caps, notDeclaredMetered);
+                initialCapsNotMetered, caps);
         assertEquals(withWifiAndMobileUnderlying, caps);
 
-        mService.applyUnderlyingCapabilities(null, caps, notDeclaredMetered);
+        mService.applyUnderlyingCapabilities(null, initialCapsNotMetered, caps);
         assertEquals(withWifiUnderlying, caps);
     }
 
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 337507a..6e380be 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -58,6 +58,7 @@
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
+import android.net.INetd;
 import android.net.Ikev2VpnProfile;
 import android.net.InetAddresses;
 import android.net.IpPrefix;
@@ -70,6 +71,7 @@
 import android.net.NetworkInfo.DetailedState;
 import android.net.RouteInfo;
 import android.net.UidRange;
+import android.net.UidRangeParcel;
 import android.net.VpnManager;
 import android.net.VpnService;
 import android.net.ipsec.ike.IkeSessionCallback;
@@ -172,11 +174,13 @@
             mPackages.put(PKGS[i], PKG_UIDS[i]);
         }
     }
+    private static final UidRange PRI_USER_RANGE = UidRange.createForUser(primaryUser.id);
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Context mContext;
     @Mock private UserManager mUserManager;
     @Mock private PackageManager mPackageManager;
     @Mock private INetworkManagementService mNetService;
+    @Mock private INetd mNetd;
     @Mock private AppOpsManager mAppOps;
     @Mock private NotificationManager mNotificationManager;
     @Mock private Vpn.SystemServices mSystemServices;
@@ -256,8 +260,7 @@
                 null, null);
 
         assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
-            UidRange.createForUser(primaryUser.id),
-            UidRange.createForUser(restrictedProfileA.id)
+                PRI_USER_RANGE, UidRange.createForUser(restrictedProfileA.id)
         })), ranges);
     }
 
@@ -269,9 +272,7 @@
         final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
                 null, null);
 
-        assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
-            UidRange.createForUser(primaryUser.id)
-        })), ranges);
+        assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] { PRI_USER_RANGE })), ranges);
     }
 
     @Test
@@ -282,15 +283,13 @@
         final Set<UidRange> ranges = new ArraySet<>();
         vpn.addUserToRanges(ranges, primaryUser.id, null, null);
 
-        assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
-            UidRange.createForUser(primaryUser.id)
-        })), ranges);
+        assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] { PRI_USER_RANGE })), ranges);
     }
 
     @Test
     public void testUidAllowAndDenylist() throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
-        final UidRange user = UidRange.createForUser(primaryUser.id);
+        final UidRange user = PRI_USER_RANGE;
         final String[] packages = {PKGS[0], PKGS[1], PKGS[2]};
 
         // Allowed list
@@ -339,62 +338,67 @@
     @Test
     public void testLockdownChangingPackage() throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
-        final UidRange user = UidRange.createForUser(primaryUser.id);
+        final UidRange user = PRI_USER_RANGE;
 
         // Default state.
-        assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
+        assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1],
+                user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
 
         // Set always-on without lockdown.
         assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null, mKeyStore));
-        assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
+        assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1],
+                user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
 
         // Set always-on with lockdown.
         assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null, mKeyStore));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
-            new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
-            new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
         }));
-        assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
+
+        assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[2],
+                user.start + PKG_UIDS[3]);
         assertUnblocked(vpn, user.start + PKG_UIDS[1]);
 
         // Switch to another app.
         assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
-            new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
-            new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
         }));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
-            new UidRange(user.start, user.start + PKG_UIDS[3] - 1),
-            new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
+
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start, user.start + PKG_UIDS[3] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
         }));
-        assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
+        assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1],
+                user.start + PKG_UIDS[2]);
         assertUnblocked(vpn, user.start + PKG_UIDS[3]);
     }
 
     @Test
     public void testLockdownAllowlist() throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
-        final UidRange user = UidRange.createForUser(primaryUser.id);
+        final UidRange user = PRI_USER_RANGE;
 
         // Set always-on with lockdown and allow app PKGS[2] from lockdown.
         assertTrue(vpn.setAlwaysOnPackage(
                 PKGS[1], true, Collections.singletonList(PKGS[2]), mKeyStore));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
-                new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
-                new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop)
         }));
         assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]);
         assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
-
         // Change allowed app list to PKGS[3].
         assertTrue(vpn.setAlwaysOnPackage(
                 PKGS[1], true, Collections.singletonList(PKGS[3]), mKeyStore));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
-                new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop)
         }));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
-                new UidRange(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1),
-                new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
         }));
         assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[2]);
         assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[3]);
@@ -402,25 +406,25 @@
         // Change the VPN app.
         assertTrue(vpn.setAlwaysOnPackage(
                 PKGS[0], true, Collections.singletonList(PKGS[3]), mKeyStore));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
-                new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
-                new UidRange(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1)
+        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1)
         }));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
-                new UidRange(user.start, user.start + PKG_UIDS[0] - 1),
-                new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1)
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start, user.start + PKG_UIDS[0] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1)
         }));
         assertBlocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
         assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]);
 
         // Remove the list of allowed packages.
         assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null, mKeyStore));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
-                new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1),
-                new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
         }));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
-                new UidRange(user.start + PKG_UIDS[0] + 1, user.stop),
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.stop),
         }));
         assertBlocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2],
                 user.start + PKG_UIDS[3]);
@@ -429,12 +433,12 @@
         // Add the list of allowed packages.
         assertTrue(vpn.setAlwaysOnPackage(
                 PKGS[0], true, Collections.singletonList(PKGS[1]), mKeyStore));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
-                new UidRange(user.start + PKG_UIDS[0] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.stop)
         }));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
-                new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
-                new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
         }));
         assertBlocked(vpn, user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
         assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1]);
@@ -447,13 +451,13 @@
         // allowed package should change from PGKS[1] to PKGS[2].
         assertTrue(vpn.setAlwaysOnPackage(
                 PKGS[0], true, Arrays.asList("com.foo.app", PKGS[2], "com.bar.app"), mKeyStore));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[]{
-                new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
-                new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[]{
+                new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
         }));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[]{
-                new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[2] - 1),
-                new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[]{
+                new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[2] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop)
         }));
     }
 
@@ -467,86 +471,86 @@
                 restrictedProfileA.flags);
         tempProfile.restrictedProfileParentId = primaryUser.id;
 
-        final UidRange user = UidRange.createForUser(primaryUser.id);
+        final UidRange user = PRI_USER_RANGE;
         final UidRange profile = UidRange.createForUser(tempProfile.id);
 
         // Set lockdown.
         assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore));
-        verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
-            new UidRange(user.start, user.start + PKG_UIDS[3] - 1),
-            new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(user.start, user.start + PKG_UIDS[3] - 1),
+                new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
         }));
-
         // Verify restricted user isn't affected at first.
         assertUnblocked(vpn, profile.start + PKG_UIDS[0]);
 
         // Add the restricted user.
         setMockedUsers(primaryUser, tempProfile);
         vpn.onUserAdded(tempProfile.id);
-        verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
-            new UidRange(profile.start, profile.start + PKG_UIDS[3] - 1),
-            new UidRange(profile.start + PKG_UIDS[3] + 1, profile.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(profile.start, profile.start + PKG_UIDS[3] - 1),
+                new UidRangeParcel(profile.start + PKG_UIDS[3] + 1, profile.stop)
         }));
 
         // Remove the restricted user.
         tempProfile.partial = true;
         vpn.onUserRemoved(tempProfile.id);
-        verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
-            new UidRange(profile.start, profile.start + PKG_UIDS[3] - 1),
-            new UidRange(profile.start + PKG_UIDS[3] + 1, profile.stop)
+        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+                new UidRangeParcel(profile.start, profile.start + PKG_UIDS[3] - 1),
+                new UidRangeParcel(profile.start + PKG_UIDS[3] + 1, profile.stop)
         }));
     }
 
     @Test
     public void testLockdownRuleRepeatability() throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
-
+        final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] {
+                new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)};
         // Given legacy lockdown is already enabled,
         vpn.setLockdown(true);
-        verify(mNetService, times(1)).setAllowOnlyVpnForUids(
-                eq(true), aryEq(new UidRange[] {UidRange.createForUser(primaryUser.id)}));
+
+        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(primaryUserRangeParcel));
 
         // Enabling legacy lockdown twice should do nothing.
         vpn.setLockdown(true);
-        verify(mNetService, times(1)).setAllowOnlyVpnForUids(anyBoolean(), any(UidRange[].class));
+        verify(mNetd, times(1))
+                .networkRejectNonSecureVpn(anyBoolean(), any(UidRangeParcel[].class));
 
         // And disabling should remove the rules exactly once.
         vpn.setLockdown(false);
-        verify(mNetService, times(1)).setAllowOnlyVpnForUids(
-                eq(false), aryEq(new UidRange[] {UidRange.createForUser(primaryUser.id)}));
+        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(primaryUserRangeParcel));
 
         // Removing the lockdown again should have no effect.
         vpn.setLockdown(false);
-        verify(mNetService, times(2)).setAllowOnlyVpnForUids(anyBoolean(), any(UidRange[].class));
+        verify(mNetd, times(2)).networkRejectNonSecureVpn(
+                anyBoolean(), any(UidRangeParcel[].class));
     }
 
     @Test
     public void testLockdownRuleReversibility() throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
-
-        final UidRange[] entireUser = {
-            UidRange.createForUser(primaryUser.id)
+        final UidRangeParcel[] entireUser = {
+            new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)
         };
-        final UidRange[] exceptPkg0 = {
-            new UidRange(entireUser[0].start, entireUser[0].start + PKG_UIDS[0] - 1),
-            new UidRange(entireUser[0].start + PKG_UIDS[0] + 1, entireUser[0].stop)
+        final UidRangeParcel[] exceptPkg0 = {
+            new UidRangeParcel(entireUser[0].start, entireUser[0].start + PKG_UIDS[0] - 1),
+            new UidRangeParcel(entireUser[0].start + PKG_UIDS[0] + 1, entireUser[0].stop)
         };
 
-        final InOrder order = inOrder(mNetService);
+        final InOrder order = inOrder(mNetd);
 
         // Given lockdown is enabled with no package (legacy VPN),
         vpn.setLockdown(true);
-        order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
+        order.verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(entireUser));
 
         // When a new VPN package is set the rules should change to cover that package.
         vpn.prepare(null, PKGS[0], VpnManager.TYPE_VPN_SERVICE);
-        order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(entireUser));
-        order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(exceptPkg0));
+        order.verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(entireUser));
+        order.verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(exceptPkg0));
 
         // When that VPN package is unset, everything should be undone again in reverse.
         vpn.prepare(null, VpnConfig.LEGACY_VPN, VpnManager.TYPE_VPN_SERVICE);
-        order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(exceptPkg0));
-        order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
+        order.verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(exceptPkg0));
+        order.verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(entireUser));
     }
 
     @Test
@@ -1186,7 +1190,7 @@
                 .thenReturn(asUserContext);
         final TestLooper testLooper = new TestLooper();
         final Vpn vpn = new Vpn(testLooper.getLooper(), mContext, new TestDeps(), mNetService,
-                userId, mKeyStore, mSystemServices, mIkev2SessionCreator);
+                mNetd, userId, mKeyStore, mSystemServices, mIkev2SessionCreator);
         verify(mConnectivityManager, times(1)).registerNetworkProvider(argThat(
                 provider -> provider.getName().contains("VpnNetworkProvider")
         ));