Merge "Fix error formatting issues"
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index 26010ef6..bdab7d0 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -545,7 +545,7 @@
                 out.attribute(null, "net-capabilities", Long.toString(
                         BitUtils.packBits(network.getCapabilities())));
                 out.attribute(null, "net-unwanted-capabilities", Long.toString(
-                        BitUtils.packBits(network.getUnwantedCapabilities())));
+                        BitUtils.packBits(network.getForbiddenCapabilities())));
 
                 out.attribute(null, "net-transport-types", Long.toString(
                         BitUtils.packBits(network.getTransportTypes())));
@@ -968,22 +968,22 @@
             String val;
 
             final String netCapabilities = parser.getAttributeValue(null, "net-capabilities");
-            final String netUnwantedCapabilities = parser.getAttributeValue(
+            final String netforbiddenCapabilities = parser.getAttributeValue(
                     null, "net-unwanted-capabilities");
             final String netTransportTypes = parser.getAttributeValue(null, "net-transport-types");
             if (netCapabilities != null && netTransportTypes != null) {
                 final NetworkRequest.Builder builder = new NetworkRequest.Builder()
                         .clearCapabilities();
-                final long unwantedCapabilities = netUnwantedCapabilities != null
-                        ? Long.parseLong(netUnwantedCapabilities)
-                        : BitUtils.packBits(builder.build().getUnwantedCapabilities());
+                final long forbiddenCapabilities = netforbiddenCapabilities != null
+                        ? Long.parseLong(netforbiddenCapabilities)
+                        : BitUtils.packBits(builder.build().getForbiddenCapabilities());
                 // We're okay throwing NFE here; caught by caller
                 for (int capability : BitUtils.unpackBits(Long.parseLong(netCapabilities))) {
                     builder.addCapability(capability);
                 }
-                for (int unwantedCapability : BitUtils.unpackBits(
-                        Long.parseLong(netUnwantedCapabilities))) {
-                    builder.addUnwantedCapability(unwantedCapability);
+                for (int forbiddenCapability : BitUtils.unpackBits(
+                        Long.parseLong(netforbiddenCapabilities))) {
+                    builder.addForbiddenCapability(forbiddenCapability);
                 }
                 for (int transport : BitUtils.unpackBits(Long.parseLong(netTransportTypes))) {
                     builder.addTransportType(transport);
diff --git a/apex/media/Android.bp b/apex/media/Android.bp
index 308741a..a75f1ae 100644
--- a/apex/media/Android.bp
+++ b/apex/media/Android.bp
@@ -24,3 +24,10 @@
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_base_license"],
 }
+
+sdk {
+    name: "media-module-sdk",
+    java_sdk_libs: [
+        "framework-media",
+    ],
+}
diff --git a/api/Android.bp b/api/Android.bp
index 2df31ec..5b2d97f 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -24,12 +24,17 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
+metalava_cmd = "$(location metalava)"
+// Silence reflection warnings. See b/168689341
+metalava_cmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
+metalava_cmd += " --no-banner --format=v2 "
+
 genrule {
     name: "current-api-xml",
     tools: ["metalava"],
     srcs: [":frameworks-base-api-current.txt"],
     out: ["current.api"],
-    cmd: "$(location metalava) --no-banner -convert2xmlnostrip $(in) $(out)",
+    cmd: metalava_cmd + "-convert2xmlnostrip $(in) $(out)",
     visibility: ["//visibility:public"],
 }
 
@@ -52,7 +57,7 @@
     ],
     out: ["current.txt"],
     tools: ["metalava"],
-    cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+    cmd: metalava_cmd + "$(in) --api $(out)",
     dists: [
         {
             targets: ["droidcore"],
@@ -77,7 +82,7 @@
     ],
     out: ["stdout.txt"],
     tools: ["metalava"],
-    cmd: "$(location metalava) --no-banner --format=v2 " +
+    cmd: metalava_cmd +
         "--check-compatibility:api:released $(location :android.api.public.latest) " +
         "--baseline:compatibility:released $(location :android-incompatibilities.api.public.latest) " +
         "$(location :frameworks-base-api-current.txt) " +
@@ -126,7 +131,7 @@
     ],
     out: ["removed.txt"],
     tools: ["metalava"],
-    cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+    cmd: metalava_cmd + "$(in) --api $(out)",
     dists: [
         {
             targets: ["droidcore"],
@@ -157,7 +162,7 @@
     ],
     out: ["system-current.txt"],
     tools: ["metalava"],
-    cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+    cmd: metalava_cmd + "$(in) --api $(out)",
     dists: [
         {
             targets: ["droidcore"],
@@ -183,7 +188,7 @@
     ],
     out: ["stdout.txt"],
     tools: ["metalava"],
-    cmd: "$(location metalava) --no-banner --format=v2 " +
+    cmd: metalava_cmd +
         "--check-compatibility:api:released $(location :android.api.system.latest) " +
         "--check-compatibility:base $(location :frameworks-base-api-current.txt) " +
         "--baseline:compatibility:released $(location :android-incompatibilities.api.system.latest) " +
@@ -207,7 +212,7 @@
     ],
     out: ["system-removed.txt"],
     tools: ["metalava"],
-    cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+    cmd: metalava_cmd + "$(in) --api $(out)",
     dists: [
         {
             targets: ["droidcore"],
@@ -239,7 +244,7 @@
     ],
     out: ["module-lib-current.txt"],
     tools: ["metalava"],
-    cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+    cmd: metalava_cmd + "$(in) --api $(out)",
     dists: [
         {
             targets: ["droidcore"],
@@ -264,7 +269,7 @@
     ],
     out: ["stdout.txt"],
     tools: ["metalava"],
-    cmd: "$(location metalava) --no-banner --format=v2 " +
+    cmd: metalava_cmd +
         "--check-compatibility:api:released $(location :android.api.module-lib.latest) " +
         // Note: having "public" be the base of module-lib is not perfect -- it should
         // ideally be a merged public+system), but this will  help when migrating from
@@ -291,7 +296,7 @@
     ],
     out: ["module-lib-removed.txt"],
     tools: ["metalava"],
-    cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+    cmd: metalava_cmd + "$(in) --api $(out)",
     dists: [
         {
             targets: ["droidcore"],
@@ -331,7 +336,7 @@
     ],
     out: ["system-server-current.txt"],
     tools: ["metalava"],
-    cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+    cmd: metalava_cmd + "$(in) --api $(out)",
     dists: [
         {
             targets: ["droidcore"],
@@ -354,7 +359,7 @@
     ],
     out: ["system-server-removed.txt"],
     tools: ["metalava"],
-    cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+    cmd: metalava_cmd + "$(in) --api $(out)",
     dists: [
         {
             targets: ["droidcore"],
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 848f480..5d23eb2 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -89,9 +89,9 @@
   public final class UnderlyingNetworkInfo implements android.os.Parcelable {
     ctor public UnderlyingNetworkInfo(int, @NonNull String, @NonNull java.util.List<java.lang.String>);
     method public int describeContents();
-    method @NonNull public String getIface();
+    method @NonNull public String getInterface();
     method public int getOwnerUid();
-    method @NonNull public java.util.List<java.lang.String> getUnderlyingIfaces();
+    method @NonNull public java.util.List<java.lang.String> getUnderlyingInterfaces();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.UnderlyingNetworkInfo> CREATOR;
   }
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 81b9428..3b759a2 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -537,6 +537,10 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ProviderInfoList> CREATOR;
   }
 
+  public final class SharedLibraryInfo implements android.os.Parcelable {
+    method @NonNull public java.util.List<java.lang.String> getAllCodePaths();
+  }
+
   public final class ShortcutInfo implements android.os.Parcelable {
     method public boolean isVisibleToPublisher();
   }
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index 3c20dca..a74c663 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -587,6 +587,10 @@
          * @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
          */
         public Builder setDeviceAddress(String deviceAddress) {
+            if (deviceAddress == null) {
+                mDeviceAddress = deviceAddress;
+                return this;
+            }
             return setDeviceAddress(deviceAddress, BluetoothDevice.ADDRESS_TYPE_PUBLIC);
         }
 
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index da2a3d8..933a0c9 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -20,6 +20,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -29,6 +30,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * This class provides information for a shared library. There are
@@ -177,7 +179,8 @@
      *
      * @hide
      */
-    public List<String> getAllCodePaths() {
+    @TestApi
+    public @NonNull List<String> getAllCodePaths() {
         if (getPath() != null) {
             // Builtin library.
             ArrayList<String> list = new ArrayList<>();
@@ -185,7 +188,7 @@
             return list;
         } else {
             // Static or dynamic library.
-            return mCodePaths;
+            return Objects.requireNonNull(mCodePaths);
         }
     }
 
diff --git a/core/java/android/hardware/hdmi/OWNERS b/core/java/android/hardware/hdmi/OWNERS
index 16c15e3..60d43fd 100644
--- a/core/java/android/hardware/hdmi/OWNERS
+++ b/core/java/android/hardware/hdmi/OWNERS
@@ -4,3 +4,4 @@
 
 marvinramin@google.com
 nchalko@google.com
+lcnathalie@google.com
diff --git a/core/java/android/net/UnderlyingNetworkInfo.java b/core/java/android/net/UnderlyingNetworkInfo.java
index 459fdac..33f9375 100644
--- a/core/java/android/net/UnderlyingNetworkInfo.java
+++ b/core/java/android/net/UnderlyingNetworkInfo.java
@@ -71,13 +71,13 @@
 
     /** Get the interface name of this network. */
     @NonNull
-    public String getIface() {
+    public String getInterface() {
         return mIface;
     }
 
     /** Get the names of the interfaces underlying this network. */
     @NonNull
-    public List<String> getUnderlyingIfaces() {
+    public List<String> getUnderlyingInterfaces() {
         return mUnderlyingIfaces;
     }
 
@@ -124,8 +124,8 @@
         if (!(o instanceof UnderlyingNetworkInfo)) return false;
         final UnderlyingNetworkInfo that = (UnderlyingNetworkInfo) o;
         return mOwnerUid == that.getOwnerUid()
-                && Objects.equals(mIface, that.getIface())
-                && Objects.equals(mUnderlyingIfaces, that.getUnderlyingIfaces());
+                && Objects.equals(mIface, that.getInterface())
+                && Objects.equals(mUnderlyingIfaces, that.getUnderlyingInterfaces());
     }
 
     @Override
diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java
index d41c0b4..caab152 100644
--- a/core/java/android/net/vcn/VcnConfig.java
+++ b/core/java/android/net/vcn/VcnConfig.java
@@ -52,12 +52,17 @@
     private static final String GATEWAY_CONNECTION_CONFIGS_KEY = "mGatewayConnectionConfigs";
     @NonNull private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs;
 
+    private static final String IS_TEST_MODE_PROFILE_KEY = "mIsTestModeProfile";
+    private final boolean mIsTestModeProfile;
+
     private VcnConfig(
             @NonNull String packageName,
-            @NonNull Set<VcnGatewayConnectionConfig> gatewayConnectionConfigs) {
+            @NonNull Set<VcnGatewayConnectionConfig> gatewayConnectionConfigs,
+            boolean isTestModeProfile) {
         mPackageName = packageName;
         mGatewayConnectionConfigs =
                 Collections.unmodifiableSet(new ArraySet<>(gatewayConnectionConfigs));
+        mIsTestModeProfile = isTestModeProfile;
 
         validate();
     }
@@ -77,6 +82,7 @@
                 new ArraySet<>(
                         PersistableBundleUtils.toList(
                                 gatewayConnectionConfigsBundle, VcnGatewayConnectionConfig::new));
+        mIsTestModeProfile = in.getBoolean(IS_TEST_MODE_PROFILE_KEY);
 
         validate();
     }
@@ -104,6 +110,15 @@
     }
 
     /**
+     * Returns whether or not this VcnConfig is restricted to test networks.
+     *
+     * @hide
+     */
+    public boolean isTestModeProfile() {
+        return mIsTestModeProfile;
+    }
+
+    /**
      * Serializes this object to a PersistableBundle.
      *
      * @hide
@@ -119,13 +134,14 @@
                         new ArrayList<>(mGatewayConnectionConfigs),
                         VcnGatewayConnectionConfig::toPersistableBundle);
         result.putPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY, gatewayConnectionConfigsBundle);
+        result.putBoolean(IS_TEST_MODE_PROFILE_KEY, mIsTestModeProfile);
 
         return result;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mPackageName, mGatewayConnectionConfigs);
+        return Objects.hash(mPackageName, mGatewayConnectionConfigs, mIsTestModeProfile);
     }
 
     @Override
@@ -136,7 +152,8 @@
 
         final VcnConfig rhs = (VcnConfig) other;
         return mPackageName.equals(rhs.mPackageName)
-                && mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs);
+                && mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs)
+                && mIsTestModeProfile == rhs.mIsTestModeProfile;
     }
 
     // Parcelable methods
@@ -172,6 +189,8 @@
         @NonNull
         private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs = new ArraySet<>();
 
+        private boolean mIsTestModeProfile = false;
+
         public Builder(@NonNull Context context) {
             Objects.requireNonNull(context, "context was null");
 
@@ -207,13 +226,29 @@
         }
 
         /**
+         * Restricts this VcnConfig to matching with test networks (only).
+         *
+         * <p>This method is for testing only, and must not be used by apps. Calling {@link
+         * VcnManager#setVcnConfig(ParcelUuid, VcnConfig)} with a VcnConfig where test-network usage
+         * is enabled will require the MANAGE_TEST_NETWORKS permission.
+         *
+         * @return this {@link Builder} instance, for chaining
+         * @hide
+         */
+        @NonNull
+        public Builder setIsTestModeProfile() {
+            mIsTestModeProfile = true;
+            return this;
+        }
+
+        /**
          * Builds and validates the VcnConfig.
          *
          * @return an immutable VcnConfig instance
          */
         @NonNull
         public VcnConfig build() {
-            return new VcnConfig(mPackageName, mGatewayConnectionConfigs);
+            return new VcnConfig(mPackageName, mGatewayConnectionConfigs, mIsTestModeProfile);
         }
     }
 }
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index be308d01..2df3e6c7 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -15,6 +15,8 @@
  */
 package android.net.vcn;
 
+import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
+
 import static com.android.internal.annotations.VisibleForTesting.Visibility;
 
 import android.annotation.IntDef;
@@ -433,6 +435,8 @@
          *     distinguish between VcnGatewayConnectionConfigs configured on a single {@link
          *     VcnConfig}. This will be used as the identifier in VcnStatusCallback invocations.
          * @param tunnelConnectionParams the IKE tunnel connection configuration
+         * @throws IllegalArgumentException if the provided IkeTunnelConnectionParams is not
+         *     configured to support MOBIKE
          * @see IkeTunnelConnectionParams
          * @see VcnManager.VcnStatusCallback#onGatewayConnectionError
          */
@@ -441,6 +445,10 @@
                 @NonNull IkeTunnelConnectionParams tunnelConnectionParams) {
             Objects.requireNonNull(gatewayConnectionName, "gatewayConnectionName was null");
             Objects.requireNonNull(tunnelConnectionParams, "tunnelConnectionParams was null");
+            if (!tunnelConnectionParams.getIkeSessionParams().hasIkeOption(IKE_OPTION_MOBIKE)) {
+                throw new IllegalArgumentException(
+                        "MOBIKE must be configured for the provided IkeSessionParams");
+            }
 
             mGatewayConnectionName = gatewayConnectionName;
             mTunnelConnectionParams = tunnelConnectionParams;
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 9d1c1ff..390c3b9 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -74,6 +74,36 @@
 public class VcnManager {
     @NonNull private static final String TAG = VcnManager.class.getSimpleName();
 
+    /**
+     * Key for WiFi entry RSSI thresholds
+     *
+     * <p>The VCN will only migrate to a Carrier WiFi network that has a signal strength greater
+     * than, or equal to this threshold.
+     *
+     * <p>WARNING: The VCN does not listen for changes to this key made after VCN startup.
+     *
+     * @hide
+     */
+    @NonNull
+    public static final String VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY =
+            "vcn_network_selection_wifi_entry_rssi_threshold";
+
+    /**
+     * Key for WiFi entry RSSI thresholds
+     *
+     * <p>If the VCN's selected Carrier WiFi network has a signal strength less than this threshold,
+     * the VCN will attempt to migrate away from the Carrier WiFi network.
+     *
+     * <p>WARNING: The VCN does not listen for changes to this key made after VCN startup.
+     *
+     * @hide
+     */
+    @NonNull
+    public static final String VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY =
+            "vcn_network_selection_wifi_exit_rssi_threshold";
+
+    // TODO: Add separate signal strength thresholds for 2.4 GHz and 5GHz
+
     private static final Map<
                     VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder>
             REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>();
diff --git a/core/java/android/net/vcn/VcnTransportInfo.java b/core/java/android/net/vcn/VcnTransportInfo.java
index 0e9ccf1..1f18184 100644
--- a/core/java/android/net/vcn/VcnTransportInfo.java
+++ b/core/java/android/net/vcn/VcnTransportInfo.java
@@ -16,23 +16,17 @@
 
 package android.net.vcn;
 
-import static android.net.NetworkCapabilities.REDACT_ALL;
-import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
+import static android.net.NetworkCapabilities.REDACT_NONE;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.net.NetworkCapabilities;
 import android.net.TransportInfo;
 import android.net.wifi.WifiInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.SubscriptionManager;
 
-import com.android.internal.annotations.VisibleForTesting;
-
 import java.util.Objects;
 
 /**
@@ -55,32 +49,17 @@
     @Nullable private final WifiInfo mWifiInfo;
     private final int mSubId;
 
-    /**
-     * The redaction scheme to use when parcelling.
-     *
-     * <p>The TransportInfo/NetworkCapabilities redaction mechanisms rely on redaction being
-     * performed at parcelling time. This means that the redaction scheme must be stored for later
-     * use.
-     *
-     * <p>Since the redaction scheme itself is not parcelled, this field is listed as a transient.
-     *
-     * <p>Defaults to REDACT_ALL when constructed using public constructors, or creating from
-     * parcels.
-     */
-    private final transient long mRedactions;
-
     public VcnTransportInfo(@NonNull WifiInfo wifiInfo) {
-        this(wifiInfo, INVALID_SUBSCRIPTION_ID, REDACT_ALL);
+        this(wifiInfo, INVALID_SUBSCRIPTION_ID);
     }
 
     public VcnTransportInfo(int subId) {
-        this(null /* wifiInfo */, subId, REDACT_ALL);
+        this(null /* wifiInfo */, subId);
     }
 
-    private VcnTransportInfo(@Nullable WifiInfo wifiInfo, int subId, long redactions) {
+    private VcnTransportInfo(@Nullable WifiInfo wifiInfo, int subId) {
         mWifiInfo = wifiInfo;
         mSubId = subId;
-        mRedactions = redactions;
     }
 
     /**
@@ -102,25 +81,14 @@
      * SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
      *
      * @return the Subscription ID if a cellular underlying Network is present, else {@link
-     *     android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID}.
+     *     android.telephony.SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
      */
     public int getSubId() {
         return mSubId;
     }
 
-    /**
-     * Gets the redaction scheme
-     *
-     * @hide
-     */
-    @VisibleForTesting(visibility = PRIVATE)
-    public long getRedaction() {
-        return mRedactions;
-    }
-
     @Override
     public int hashCode() {
-        // mRedactions not hashed, as it is a transient, for control of parcelling
         return Objects.hash(mWifiInfo, mSubId);
     }
 
@@ -128,8 +96,6 @@
     public boolean equals(Object o) {
         if (!(o instanceof VcnTransportInfo)) return false;
         final VcnTransportInfo that = (VcnTransportInfo) o;
-
-        // mRedactions not compared, as it is a transient, for control of parcelling
         return Objects.equals(mWifiInfo, that.mWifiInfo) && mSubId == that.mSubId;
     }
 
@@ -143,31 +109,19 @@
     @NonNull
     public TransportInfo makeCopy(long redactions) {
         return new VcnTransportInfo(
-                mWifiInfo == null ? null : mWifiInfo.makeCopy(redactions), mSubId, redactions);
+                (mWifiInfo == null) ? null : mWifiInfo.makeCopy(redactions), mSubId);
     }
 
     @Override
     public long getApplicableRedactions() {
-        long redactions = REDACT_FOR_NETWORK_SETTINGS;
-
-        // Add additional wifi redactions if necessary
-        if (mWifiInfo != null) {
-            redactions |= mWifiInfo.getApplicableRedactions();
-        }
-
-        return redactions;
-    }
-
-    private boolean shouldParcelNetworkSettingsFields() {
-        return (mRedactions & NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS) == 0;
+        return (mWifiInfo == null) ? REDACT_NONE : mWifiInfo.getApplicableRedactions();
     }
 
     /** {@inheritDoc} */
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeInt(shouldParcelNetworkSettingsFields() ? mSubId : INVALID_SUBSCRIPTION_ID);
-        dest.writeParcelable(
-                shouldParcelNetworkSettingsFields() ? (Parcelable) mWifiInfo : null, flags);
+        dest.writeInt(mSubId);
+        dest.writeParcelable(mWifiInfo, flags);
     }
 
     @Override
@@ -181,17 +135,7 @@
                 public VcnTransportInfo createFromParcel(Parcel in) {
                     final int subId = in.readInt();
                     final WifiInfo wifiInfo = in.readParcelable(null);
-
-                    // If all fields are their null values, return null TransportInfo to avoid
-                    // leaking information about this being a VCN Network (instead of macro
-                    // cellular, etc)
-                    if (wifiInfo == null && subId == INVALID_SUBSCRIPTION_ID) {
-                        return null;
-                    }
-
-                    // Prevent further forwarding by redacting everything in future parcels from
-                    // this VcnTransportInfo
-                    return new VcnTransportInfo(wifiInfo, subId, REDACT_ALL);
+                    return new VcnTransportInfo(wifiInfo, subId);
                 }
 
                 public VcnTransportInfo[] newArray(int size) {
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index d966595..cd02d29 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -63,3 +63,6 @@
 
 # RecoverySystem
 per-file *Recovery* = file:/services/core/java/com/android/server/recoverysystem/OWNERS
+
+# Bugreporting
+per-file Bugreport* = file:/platform/frameworks/native:/cmds/dumpstate/OWNERS
diff --git a/core/java/com/android/internal/os/BinderLatencyBuckets.java b/core/java/com/android/internal/os/BinderLatencyBuckets.java
new file mode 100644
index 0000000..bdee4ca
--- /dev/null
+++ b/core/java/com/android/internal/os/BinderLatencyBuckets.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 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 com.android.internal.os;
+
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Generates the bucket thresholds (with a custom logarithmic scale) for a histogram to store
+ * latency samples in.
+ */
+public class BinderLatencyBuckets {
+    private static final String TAG = "BinderLatencyBuckets";
+    private ArrayList<Integer> mBuckets;
+
+    /**
+     * @param bucketCount      the number of buckets the histogram should have
+     * @param firstBucketSize  the size of the first bucket (used to avoid excessive small buckets)
+     * @param scaleFactor      the rate in which each consecutive bucket increases (before rounding)
+     */
+    public BinderLatencyBuckets(int bucketCount, int firstBucketSize, float scaleFactor) {
+        mBuckets = new ArrayList<>(bucketCount - 1);
+        mBuckets.add(firstBucketSize);
+
+        // Last value and the target are disjoint as we never want to create buckets smaller than 1.
+        double lastTarget = firstBucketSize;
+        int lastValue = firstBucketSize;
+
+        // First bucket is already created and the last bucket is anything greater than the final
+        // bucket in the list, so create 'bucketCount' - 2 buckets.
+        for (int i = 1; i < bucketCount - 1; i++) {
+            // Increase the target bucket limit value by the scale factor.
+            double nextTarget = lastTarget * scaleFactor;
+
+            if (nextTarget > Integer.MAX_VALUE || lastValue == Integer.MAX_VALUE) {
+                // Do not throw an exception here as this should not affect binder calls.
+                Slog.w(TAG, "Attempted to create a bucket larger than maxint");
+                return;
+            }
+
+            if ((int) nextTarget > lastValue) {
+                // Convert the target bucket limit value to an integer.
+                mBuckets.add((int) nextTarget);
+                lastValue = (int) nextTarget;
+            } else {
+                // Avoid creating redundant buckets, so bucket size should be 1 at a minimum.
+                mBuckets.add(lastValue + 1);
+                lastValue = lastValue + 1;
+            }
+            lastTarget = nextTarget;
+        }
+    }
+
+    /** Gets the bucket index to insert the provided sample in. */
+    public int sampleToBucket(int sample) {
+        if (sample > mBuckets.get(mBuckets.size() - 1)) {
+            return mBuckets.size();
+        }
+
+        // Binary search returns the element index if it is contained in the list - in this case the
+        // correct bucket is the index after as we use [minValue, maxValue) for bucket boundaries.
+        // Otherwise, it returns (-(insertion point) - 1), where insertion point is the point where
+        // to insert the element so that the array remains sorted - in this case the bucket index
+        // is the insertion point.
+        int searchResult = Collections.binarySearch(mBuckets, sample);
+        return searchResult < 0 ? -(1 + searchResult) : searchResult + 1;
+    }
+
+    @VisibleForTesting
+    public ArrayList<Integer> getBuckets() {
+        return mBuckets;
+    }
+}
diff --git a/core/java/com/android/internal/os/BinderLatencyObserver.java b/core/java/com/android/internal/os/BinderLatencyObserver.java
index 92b4952..59cc66d 100644
--- a/core/java/com/android/internal/os/BinderLatencyObserver.java
+++ b/core/java/com/android/internal/os/BinderLatencyObserver.java
@@ -26,7 +26,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BinderInternal.CallSession;
 
-import java.util.ArrayList;
 import java.util.Random;
 
 /** Collects statistics about Binder call latency per calling API and method. */
@@ -34,18 +33,25 @@
     private static final String TAG = "BinderLatencyObserver";
     public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 10;
 
-    // This is not the final data structure - we will eventually store latency histograms instead of
-    // raw samples as it is much more memory / disk space efficient.
-    // TODO(b/179999191): change this to store the histogram.
-    // TODO(b/179999191): pre allocate the array size so we would not have to resize this.
+    // Histogram buckets parameters.
+    public static final int BUCKET_COUNT_DEFAULT = 100;
+    public static final int FIRST_BUCKET_SIZE_DEFAULT = 5;
+    public static final float BUCKET_SCALE_FACTOR_DEFAULT = 1.125f;
+
     @GuardedBy("mLock")
-    private final ArrayMap<LatencyDims, ArrayList<Long>> mLatencySamples = new ArrayMap<>();
+    private final ArrayMap<LatencyDims, int[]> mLatencyHistograms = new ArrayMap<>();
     private final Object mLock = new Object();
 
     // Sampling period to control how often to track CPU usage. 1 means all calls, 100 means ~1 out
     // of 100 requests.
     private int mPeriodicSamplingInterval = PERIODIC_SAMPLING_INTERVAL_DEFAULT;
+
+    private int mBucketCount = BUCKET_COUNT_DEFAULT;
+    private int mFirstBucketSize = FIRST_BUCKET_SIZE_DEFAULT;
+    private float mBucketScaleFactor = BUCKET_SCALE_FACTOR_DEFAULT;
+
     private final Random mRandom;
+    private BinderLatencyBuckets mLatencyBuckets;
 
     /** Injector for {@link BinderLatencyObserver}. */
     public static class Injector {
@@ -56,6 +62,8 @@
 
     public BinderLatencyObserver(Injector injector) {
         mRandom = injector.getRandomGenerator();
+        mLatencyBuckets = new BinderLatencyBuckets(
+            mBucketCount, mFirstBucketSize, mBucketScaleFactor);
     }
 
     /** Should be called when a Binder call completes, will store latency data. */
@@ -67,12 +75,21 @@
         LatencyDims dims = new LatencyDims(s.binderClass, s.transactionCode);
         long callDuration = getElapsedRealtimeMicro() - s.timeStarted;
 
+        // Find the bucket this sample should go to.
+        int bucketIdx = mLatencyBuckets.sampleToBucket(
+                callDuration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) callDuration);
+
         synchronized (mLock) {
-            if (!mLatencySamples.containsKey(dims)) {
-                mLatencySamples.put(dims, new ArrayList<Long>());
+            int[] buckets = mLatencyHistograms.get(dims);
+            if (buckets == null) {
+                buckets = new int[mBucketCount];
+                mLatencyHistograms.put(dims, buckets);
             }
 
-            mLatencySamples.get(dims).add(callDuration);
+            // Increment the correct bucket.
+            if (buckets[bucketIdx] < Integer.MAX_VALUE) {
+                buckets[bucketIdx] += 1;
+            }
         }
     }
 
@@ -100,10 +117,26 @@
         }
     }
 
+    /** Updates the histogram buckets parameters. */
+    public void setHistogramBucketsParams(
+            int bucketCount, int firstBucketSize, float bucketScaleFactor) {
+        synchronized (mLock) {
+            if (bucketCount != mBucketCount || firstBucketSize != mFirstBucketSize
+                    || bucketScaleFactor != mBucketScaleFactor) {
+                mBucketCount = bucketCount;
+                mFirstBucketSize = firstBucketSize;
+                mBucketScaleFactor = bucketScaleFactor;
+                mLatencyBuckets = new BinderLatencyBuckets(
+                    mBucketCount, mFirstBucketSize, mBucketScaleFactor);
+                reset();
+            }
+        }
+    }
+
     /** Resets the sample collection. */
     public void reset() {
         synchronized (mLock) {
-            mLatencySamples.clear();
+            mLatencyHistograms.clear();
         }
     }
 
@@ -151,7 +184,7 @@
     }
 
     @VisibleForTesting
-    public ArrayMap<LatencyDims, ArrayList<Long>> getLatencySamples() {
-        return mLatencySamples;
+    public ArrayMap<LatencyDims, int[]> getLatencyHistograms() {
+        return mLatencyHistograms;
     }
 }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index afd19b6..382acc0 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -149,7 +149,6 @@
                 "android_os_VintfRuntimeInfo.cpp",
                 "android_os_incremental_IncrementalManager.cpp",
                 "android_net_LocalSocketImpl.cpp",
-                "android_net_NetworkUtils.cpp",
                 "android_service_DataLoaderService.cpp",
                 "android_util_AssetManager.cpp",
                 "android_util_Binder.cpp",
@@ -216,6 +215,7 @@
 
             static_libs: [
                 "libasync_safe",
+                "libconnectivityframeworkutils",
                 "libbinderthreadstateutils",
                 "libdmabufinfo",
                 "libgif",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 357f427..4a57448 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -398,6 +398,8 @@
     <protected-broadcast android:name="android.net.wifi.p2p.action.WIFI_P2P_PERSISTENT_GROUPS_CHANGED" />
     <protected-broadcast android:name="android.net.conn.TETHER_STATE_CHANGED" />
     <protected-broadcast android:name="android.net.conn.INET_CONDITION_ACTION" />
+    <!-- This broadcast is no longer sent in S but it should stay protected to avoid third party
+         apps broadcasting this and confusing old system apps that may not have been updated. -->
     <protected-broadcast android:name="android.net.conn.NETWORK_CONDITIONS_MEASURED" />
     <protected-broadcast
             android:name="android.net.ConnectivityService.action.PKT_CNT_SAMPLE_INTERVAL_ELAPSED" />
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 1156120..cec6216 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -764,7 +764,7 @@
         bcs.elapsedTime += 20;
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
 
-        assertEquals(1, bcs.getLatencyObserver().getLatencySamples().size());
+        assertEquals(1, bcs.getLatencyObserver().getLatencyHistograms().size());
     }
 
     @Test
@@ -778,7 +778,7 @@
         bcs.elapsedTime += 20;
         bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
 
-        assertEquals(0, bcs.getLatencyObserver().getLatencySamples().size());
+        assertEquals(0, bcs.getLatencyObserver().getLatencyHistograms().size());
     }
 
     class TestBinderCallsStats extends BinderCallsStats {
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderLatencyBucketsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderLatencyBucketsTest.java
new file mode 100644
index 0000000..00443a9
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/BinderLatencyBucketsTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 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 com.android.internal.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public class BinderLatencyBucketsTest {
+    @Test
+    public void testBucketThresholds() {
+        BinderLatencyBuckets latencyBuckets = new BinderLatencyBuckets(10, 2, 1.45f);
+        assertThat(latencyBuckets.getBuckets())
+            .containsExactly(2, 3, 4, 6, 8, 12, 18, 26, 39)
+            .inOrder();
+    }
+
+    @Test
+    public void testSampleAssignment() {
+        BinderLatencyBuckets latencyBuckets = new BinderLatencyBuckets(10, 2, 1.45f);
+        assertEquals(0, latencyBuckets.sampleToBucket(0));
+        assertEquals(0, latencyBuckets.sampleToBucket(1));
+        assertEquals(1, latencyBuckets.sampleToBucket(2));
+        assertEquals(2, latencyBuckets.sampleToBucket(3));
+        assertEquals(3, latencyBuckets.sampleToBucket(4));
+        assertEquals(5, latencyBuckets.sampleToBucket(9));
+        assertEquals(6, latencyBuckets.sampleToBucket(13));
+        assertEquals(7, latencyBuckets.sampleToBucket(25));
+        assertEquals(9, latencyBuckets.sampleToBucket(100));
+    }
+
+    @Test
+    public void testMaxIntBuckets() {
+        BinderLatencyBuckets latencyBuckets = new BinderLatencyBuckets(5, Integer.MAX_VALUE / 2, 2);
+        assertThat(latencyBuckets.getBuckets())
+            .containsExactly(Integer.MAX_VALUE / 2, Integer.MAX_VALUE - 1)
+            .inOrder();
+
+        assertEquals(0, latencyBuckets.sampleToBucket(0));
+        assertEquals(0, latencyBuckets.sampleToBucket(Integer.MAX_VALUE / 2 - 1));
+        assertEquals(1, latencyBuckets.sampleToBucket(Integer.MAX_VALUE - 2));
+        assertEquals(2, latencyBuckets.sampleToBucket(Integer.MAX_VALUE));
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java
index 36915a2..f65fb95 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java
@@ -33,7 +33,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Random;
 
@@ -44,6 +43,7 @@
     @Test
     public void testLatencyCollectionWithMultipleClasses() {
         TestBinderLatencyObserver blo = new TestBinderLatencyObserver();
+        blo.setHistogramBucketsParams(5, 5, 1.125f);
 
         Binder binder = new Binder();
         CallSession callSession = new CallSession();
@@ -51,20 +51,24 @@
         callSession.transactionCode = 1;
         blo.callEnded(callSession);
         blo.callEnded(callSession);
+        blo.callEnded(callSession);
         callSession.transactionCode = 2;
         blo.callEnded(callSession);
+        blo.callEnded(callSession);
 
-        ArrayMap<LatencyDims, ArrayList<Long>> latencySamples = blo.getLatencySamples();
-        assertEquals(2, latencySamples.keySet().size());
-        assertThat(latencySamples.get(new LatencyDims(binder.getClass(), 1)))
-            .containsExactlyElementsIn(Arrays.asList(1L, 2L));
-        assertThat(latencySamples.get(new LatencyDims(binder.getClass(), 2))).containsExactly(3L);
+        ArrayMap<LatencyDims, int[]> latencyHistograms = blo.getLatencyHistograms();
+        assertEquals(2, latencyHistograms.keySet().size());
+        assertThat(latencyHistograms.get(new LatencyDims(binder.getClass(), 1)))
+            .asList().containsExactly(2, 0, 1, 0, 0).inOrder();
+        assertThat(latencyHistograms.get(new LatencyDims(binder.getClass(), 2)))
+            .asList().containsExactly(0, 0, 0, 0, 2).inOrder();
     }
 
     @Test
     public void testSampling() {
         TestBinderLatencyObserver blo = new TestBinderLatencyObserver();
         blo.setSamplingInterval(2);
+        blo.setHistogramBucketsParams(5, 5, 1.125f);
 
         Binder binder = new Binder();
         CallSession callSession = new CallSession();
@@ -74,17 +78,58 @@
         callSession.transactionCode = 2;
         blo.callEnded(callSession);
 
-        ArrayMap<LatencyDims, ArrayList<Long>> latencySamples = blo.getLatencySamples();
-        assertEquals(1, latencySamples.size());
-        LatencyDims dims = latencySamples.keySet().iterator().next();
+        ArrayMap<LatencyDims, int[]> latencyHistograms = blo.getLatencyHistograms();
+        assertEquals(1, latencyHistograms.size());
+        LatencyDims dims = latencyHistograms.keySet().iterator().next();
         assertEquals(binder.getClass(), dims.getBinderClass());
         assertEquals(1, dims.getTransactionCode());
-        ArrayList<Long> values = latencySamples.get(dims);
-        assertThat(values).containsExactly(1L);
+        assertThat(latencyHistograms.get(dims)).asList().containsExactly(1, 0, 0, 0, 0).inOrder();
+    }
+
+    @Test
+    public void testTooCallLengthOverflow() {
+        TestBinderLatencyObserver blo = new TestBinderLatencyObserver();
+        blo.setElapsedTime(2L + (long) Integer.MAX_VALUE);
+        blo.setHistogramBucketsParams(5, 5, 1.125f);
+
+        Binder binder = new Binder();
+        CallSession callSession = new CallSession();
+        callSession.binderClass = binder.getClass();
+        callSession.transactionCode = 1;
+        blo.callEnded(callSession);
+
+        // The long call should be capped to maxint (to not overflow) and placed in the last bucket.
+        assertThat(blo.getLatencyHistograms()
+            .get(new LatencyDims(binder.getClass(), 1)))
+            .asList().containsExactly(0, 0, 0, 0, 1)
+            .inOrder();
+    }
+
+    @Test
+    public void testHistogramBucketOverflow() {
+        TestBinderLatencyObserver blo = new TestBinderLatencyObserver();
+        blo.setHistogramBucketsParams(3, 5, 1.125f);
+
+        Binder binder = new Binder();
+        CallSession callSession = new CallSession();
+        callSession.binderClass = binder.getClass();
+        callSession.transactionCode = 1;
+        blo.callEnded(callSession);
+
+        LatencyDims dims = new LatencyDims(binder.getClass(), 1);
+        // Fill the buckets with maxint.
+        Arrays.fill(blo.getLatencyHistograms().get(dims), Integer.MAX_VALUE);
+        assertThat(blo.getLatencyHistograms().get(dims))
+            .asList().containsExactly(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
+        // Try to add another sample.
+        blo.callEnded(callSession);
+        // Make sure the buckets don't overflow.
+        assertThat(blo.getLatencyHistograms().get(dims))
+            .asList().containsExactly(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
     }
 
     public static class TestBinderLatencyObserver extends BinderLatencyObserver {
-        private long mElapsedTimeCallCount = 0;
+        private long mElapsedTime = 0;
 
         TestBinderLatencyObserver() {
             // Make random generator not random.
@@ -104,7 +149,12 @@
 
         @Override
         protected long getElapsedRealtimeMicro() {
-            return ++mElapsedTimeCallCount;
+            mElapsedTime += 2;
+            return mElapsedTime;
+        }
+
+        public void setElapsedTime(long time) {
+            mElapsedTime = time;
         }
     }
 }
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index df579bb..1034847 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.Disabled;
+import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
@@ -140,6 +141,7 @@
         if (mBinder == null || retryLookup) {
             mBinder = IKeystoreService.Stub.asInterface(ServiceManager
                     .getService(KEYSTORE2_SERVICE_NAME));
+            Binder.allowBlocking(mBinder.asBinder());
         }
         return mBinder;
     }
diff --git a/keystore/java/android/security/KeyStoreOperation.java b/keystore/java/android/security/KeyStoreOperation.java
index a6552dd..e6c1ea8 100644
--- a/keystore/java/android/security/KeyStoreOperation.java
+++ b/keystore/java/android/security/KeyStoreOperation.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.hardware.security.keymint.KeyParameter;
+import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.security.keymaster.KeymasterDefs;
@@ -39,6 +40,7 @@
             Long challenge,
             KeyParameter[] parameters
     ) {
+        Binder.allowBlocking(operation.asBinder());
         this.mOperation = operation;
         this.mChallenge = challenge;
         this.mParameters = parameters;
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
index d188b65..b85dd74 100644
--- a/keystore/java/android/security/KeyStoreSecurityLevel.java
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.app.compat.CompatChanges;
 import android.hardware.security.keymint.KeyParameter;
+import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.security.keystore.BackendBusyException;
@@ -45,6 +46,7 @@
     private final IKeystoreSecurityLevel mSecurityLevel;
 
     public KeyStoreSecurityLevel(IKeystoreSecurityLevel securityLevel) {
+        Binder.allowBlocking(securityLevel.asBinder());
         this.mSecurityLevel = securityLevel;
     }
 
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
index fe05989..97592b4 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -252,7 +252,9 @@
                 blockModes,
                 userAuthenticationRequired,
                 (int) userAuthenticationValidityDurationSeconds,
-                keymasterHwEnforcedUserAuthenticators,
+                userAuthenticationRequirementEnforcedBySecureHardware
+                        ? keymasterHwEnforcedUserAuthenticators
+                        : keymasterSwEnforcedUserAuthenticators,
                 userAuthenticationRequirementEnforcedBySecureHardware,
                 userAuthenticationValidWhileOnBody,
                 trustedUserPresenceRequired,
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 3e2fb94..f3cfcf1 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -41,6 +41,8 @@
 import android.system.keystore2.ResponseCode;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -974,7 +976,6 @@
     }
 
     private Set<String> getUniqueAliases() {
-
         try {
             final KeyDescriptor[] keys = mKeyStore.list(
                     getTargetDomain(),
@@ -987,7 +988,7 @@
             return aliases;
         } catch (android.security.KeyStoreException e) {
             Log.e(TAG, "Failed to list keystore entries.", e);
-            return null;
+            return new HashSet<>();
         }
     }
 
@@ -1099,6 +1100,17 @@
         return caAlias;
     }
 
+    /**
+     * Used by Tests to initialize with a fake KeyStore2.
+     * @hide
+     * @param keystore
+     */
+    @VisibleForTesting
+    public void initForTesting(KeyStore2 keystore) {
+        mKeyStore = keystore;
+        mNamespace = KeyProperties.NAMESPACE_APPLICATION;
+    }
+
     @Override
     public void engineStore(OutputStream stream, char[] password) throws IOException,
             NoSuchAlgorithmException, CertificateException {
diff --git a/keystore/tests/Android.bp b/keystore/tests/Android.bp
index 2315a85..7de4523 100644
--- a/keystore/tests/Android.bp
+++ b/keystore/tests/Android.bp
@@ -28,6 +28,7 @@
     static_libs: [
         "androidx.test.rules",
         "hamcrest-library",
+        "mockito-target-minus-junit4",
     ],
     platform_apis: true,
     libs: ["android.test.runner"],
diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
index b7d72fc..2ae61ab 100644
--- a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
+++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
@@ -43,7 +43,6 @@
     static final String ALIAS = "keystore-alias";
     static final String ANOTHER_ALIAS = "another-keystore-alias";
     static final int KEY_PURPOSES = KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY;
-    static final int UID = 1230;
     static final int KEYSIZE = 2048;
     static final X500Principal SUBJECT = new X500Principal("CN=subject");
     static final BigInteger SERIAL = new BigInteger("1234567890");
@@ -61,7 +60,7 @@
 
     public static KeyGenParameterSpec configureDefaultSpec() {
         return new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
-                .setUid(UID)
+                .setNamespace(KeyProperties.NAMESPACE_WIFI)
                 .setKeySize(KEYSIZE)
                 .setCertificateSubject(SUBJECT)
                 .setCertificateSerialNumber(SERIAL)
@@ -88,10 +87,11 @@
                 .build();
     }
 
-    public static void validateSpecValues(KeyGenParameterSpec spec, int uid, String alias) {
+    public static void validateSpecValues(KeyGenParameterSpec spec,
+            @KeyProperties.Namespace int namespace, String alias) {
         assertThat(spec.getKeystoreAlias(), is(alias));
         assertThat(spec.getPurposes(), is(KEY_PURPOSES));
-        assertThat(spec.getUid(), is(uid));
+        assertThat(spec.getNamespace(), is(namespace));
         assertThat(spec.getKeySize(), is(KEYSIZE));
         assertThat(spec.getCertificateSubject(), is(SUBJECT));
         assertThat(spec.getCertificateSerialNumber(), is(SERIAL));
@@ -134,7 +134,7 @@
         Parcel parcel = parcelForReading(spec);
         ParcelableKeyGenParameterSpec fromParcel =
             ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel);
-        validateSpecValues(fromParcel.getSpec(), UID, ALIAS);
+        validateSpecValues(fromParcel.getSpec(), KeyProperties.NAMESPACE_WIFI, ALIAS);
         assertThat(parcel.dataAvail(), is(0));
     }
 
diff --git a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
index b2edfd0..ddbb1d8 100644
--- a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
+++ b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
@@ -21,8 +21,6 @@
 import static org.junit.Assert.assertThat;
 
 import android.security.ParcelableKeyGenParameterSpecTest;
-import android.security.keystore.KeyGenParameterSpec;
-import android.security.keystore.KeyProperties;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -41,7 +39,7 @@
         KeyGenParameterSpec copiedSpec =
                 new KeyGenParameterSpec.Builder(spec).build();
         ParcelableKeyGenParameterSpecTest.validateSpecValues(
-                copiedSpec, spec.getUid(), spec.getKeystoreAlias());
+                copiedSpec, spec.getNamespace(), spec.getKeystoreAlias());
     }
 
     @Test
diff --git a/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java
new file mode 100644
index 0000000..1bd3069
--- /dev/null
+++ b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 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.security.keystore2;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.security.KeyStore2;
+import android.security.KeyStoreException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class AndroidKeyStoreSpiTest {
+
+    @Mock
+    private KeyStore2 mKeystore2;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testEngineAliasesReturnsEmptySetOnKeyStoreError() throws Exception {
+        when(mKeystore2.list(anyInt(), anyLong()))
+                .thenThrow(new KeyStoreException(6, "Some Error"));
+        AndroidKeyStoreSpi spi = new AndroidKeyStoreSpi();
+        spi.initForTesting(mKeystore2);
+
+        assertThat("Empty collection expected", !spi.engineAliases().hasMoreElements());
+
+        verify(mKeystore2).list(anyInt(), anyLong());
+    }
+
+}
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index 139474c..67a040d 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -28,6 +28,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.telephony.PhoneNumberUtils;
 import android.telephony.PhoneStateListener;
 import android.telephony.TelephonyManager;
 import android.util.Log;
@@ -160,7 +161,7 @@
                        be set to true when the phone is having emergency call, and then will
                        be set to false by mPhoneStateListener when the emergency call ends.
                 */
-                mIsInEmergencyCall = mTelephonyManager.isEmergencyNumber(phoneNumber);
+                mIsInEmergencyCall = PhoneNumberUtils.isEmergencyNumber(phoneNumber);
                 if (DEBUG) Log.v(TAG, "ACTION_NEW_OUTGOING_CALL - " + getInEmergency());
             } else if (action.equals(LocationManager.MODE_CHANGED_ACTION)) {
                 updateLocationMode();
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 2d9bdaf..3131965 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -127,6 +127,9 @@
     public static final int SAMPLE_RATE_HZ_MIN = native_getMinSampleRate();
     private static native int native_getMinSampleRate();
 
+    /** @hide */
+    public static final int FCC_24 = 24; // fixed channel count 24; do not change.
+
     // Expose only the getter method publicly so we can change it in the future
     private static final int NUM_STREAM_TYPES = 12;
 
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 7c91f52..68ce5e7 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1712,9 +1712,10 @@
                 mChannelCount = 0;
                 break; // channel index configuration only
             }
-            if (!isMultichannelConfigSupported(channelConfig)) {
-                // input channel configuration features unsupported channels
-                throw new IllegalArgumentException("Unsupported channel configuration.");
+            if (!isMultichannelConfigSupported(channelConfig, audioFormat)) {
+                throw new IllegalArgumentException(
+                        "Unsupported channel mask configuration " + channelConfig
+                        + " for encoding " + audioFormat);
             }
             mChannelMask = channelConfig;
             mChannelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);
@@ -1722,13 +1723,17 @@
         // check the channel index configuration (if present)
         mChannelIndexMask = channelIndexMask;
         if (mChannelIndexMask != 0) {
-            // restrictive: indexMask could allow up to AUDIO_CHANNEL_BITS_LOG2
-            final int indexMask = (1 << AudioSystem.OUT_CHANNEL_COUNT_MAX) - 1;
-            if ((channelIndexMask & ~indexMask) != 0) {
-                throw new IllegalArgumentException("Unsupported channel index configuration "
-                        + channelIndexMask);
+            // As of S, we accept up to 24 channel index mask.
+            final int fullIndexMask = (1 << AudioSystem.FCC_24) - 1;
+            final int channelIndexCount = Integer.bitCount(channelIndexMask);
+            final boolean accepted = (channelIndexMask & ~fullIndexMask) == 0
+                    && (!AudioFormat.isEncodingLinearFrames(audioFormat)  // compressed OK
+                            || channelIndexCount <= AudioSystem.OUT_CHANNEL_COUNT_MAX); // PCM
+            if (!accepted) {
+                throw new IllegalArgumentException(
+                        "Unsupported channel index mask configuration " + channelIndexMask
+                        + " for encoding " + audioFormat);
             }
-            int channelIndexCount = Integer.bitCount(channelIndexMask);
             if (mChannelCount == 0) {
                  mChannelCount = channelIndexCount;
             } else if (mChannelCount != channelIndexCount) {
@@ -1781,16 +1786,19 @@
      * @param channelConfig the mask to validate
      * @return false if the AudioTrack can't be used with such a mask
      */
-    private static boolean isMultichannelConfigSupported(int channelConfig) {
+    private static boolean isMultichannelConfigSupported(int channelConfig, int encoding) {
         // check for unsupported channels
         if ((channelConfig & SUPPORTED_OUT_CHANNELS) != channelConfig) {
             loge("Channel configuration features unsupported channels");
             return false;
         }
         final int channelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);
-        if (channelCount > AudioSystem.OUT_CHANNEL_COUNT_MAX) {
-            loge("Channel configuration contains too many channels " +
-                    channelCount + ">" + AudioSystem.OUT_CHANNEL_COUNT_MAX);
+        final int channelCountLimit = AudioFormat.isEncodingLinearFrames(encoding)
+                ? AudioSystem.OUT_CHANNEL_COUNT_MAX  // PCM limited to OUT_CHANNEL_COUNT_MAX
+                : AudioSystem.FCC_24;                // Compressed limited to 24 channels
+        if (channelCount > channelCountLimit) {
+            loge("Channel configuration contains too many channels for encoding "
+                    + encoding + "(" + channelCount + " > " + channelCountLimit + ")");
             return false;
         }
         // check for unsupported multichannel combinations:
@@ -2301,7 +2309,7 @@
             channelCount = 2;
             break;
         default:
-            if (!isMultichannelConfigSupported(channelConfig)) {
+            if (!isMultichannelConfigSupported(channelConfig, audioFormat)) {
                 loge("getMinBufferSize(): Invalid channel configuration.");
                 return ERROR_BAD_VALUE;
             } else {
diff --git a/packages/Connectivity/framework/Android.bp b/packages/Connectivity/framework/Android.bp
index 017ff51..657d5a3 100644
--- a/packages/Connectivity/framework/Android.bp
+++ b/packages/Connectivity/framework/Android.bp
@@ -25,6 +25,7 @@
 
 java_library {
     name: "framework-connectivity-protos",
+    sdk_version: "module_current",
     proto: {
         type: "nano",
     },
@@ -82,8 +83,7 @@
     name: "framework-connectivity",
     api_only: true,
     defaults: ["framework-module-defaults"],
-    // TODO: build against module API
-    platform_apis: true,
+    installable: true,
     srcs: [
         ":framework-connectivity-sources",
     ],
@@ -100,18 +100,56 @@
     libs: [
         "unsupportedappusage",
     ],
-    permitted_packages: ["android.net", "com.android.connectivity.aidl"],
+    permitted_packages: ["android.net"],
+}
+
+cc_defaults {
+    name: "libframework-connectivity-defaults",
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+        "-Wthread-safety",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libnativehelper",
+        "libnetd_client",
+    ],
+    header_libs: [
+        "dnsproxyd_protocol_headers",
+    ],
+}
+
+cc_library_static {
+    name: "libconnectivityframeworkutils",
+    defaults: ["libframework-connectivity-defaults"],
+    srcs: [
+        "jni/android_net_NetworkUtils.cpp",
+    ],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.tethering",
+    ],
+}
+
+cc_library_shared {
+    name: "libframework-connectivity-jni",
+    defaults: ["libframework-connectivity-defaults"],
+    srcs: [
+        "jni/onload.cpp",
+    ],
+    static_libs: ["libconnectivityframeworkutils"],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.tethering",
+    ],
 }
 
 java_library {
     name: "framework-connectivity.impl",
-    // Instead of building against private API (framework.jar),
-    // build against core_platform + framework-minus-apex + module
-    // stub libs. This allows framework.jar to depend on this library,
-    // so it can be part of the private API until all clients have been migrated.
-    // TODO: just build against module_api, and remove this jar from
-    // the private API.
-    sdk_version: "core_platform",
+    sdk_version: "module_current",
     srcs: [
         ":framework-connectivity-sources",
     ],
@@ -122,12 +160,11 @@
         ],
     },
     libs: [
-        "framework-minus-apex",
-        // TODO: just framework-tethering, framework-wifi when building against module_api
-        "framework-tethering.stubs.module_lib",
-        "framework-wifi.stubs.module_lib",
+        // TODO (b/183097033) remove once module_current includes core_current
+        "stable.core.platform.api.stubs",
+        "framework-tethering",
+        "framework-wifi",
         "unsupportedappusage",
-        "ServiceConnectivityResources",
     ],
     static_libs: [
         "framework-connectivity-protos",
@@ -136,5 +173,5 @@
     jarjar_rules: "jarjar-rules.txt",
     apex_available: ["com.android.tethering"],
     installable: true,
-    permitted_packages: ["android.net", "com.android.connectivity.aidl"],
+    permitted_packages: ["android.net"],
 }
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index a8e2517..6c454bc 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -10,7 +10,6 @@
     method @NonNull @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public java.util.List<android.net.NetworkStateSnapshot> getAllNetworkStateSnapshots();
     method @Nullable public android.net.ProxyInfo getGlobalProxy();
     method @NonNull public static android.util.Range<java.lang.Integer> getIpSecNetIdRange();
-    method @NonNull public static String getPrivateDnsMode(@NonNull android.content.Context);
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerDefaultNetworkCallbackForUid(int, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
@@ -20,7 +19,6 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAvoidUnvalidated(@NonNull android.net.Network);
     method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setGlobalProxy(@Nullable android.net.ProxyInfo);
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setLegacyLockdownVpnEnabled(boolean);
-    method public static void setPrivateDnsMode(@NonNull android.content.Context, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setProfileNetworkPreference(@NonNull android.os.UserHandle, int, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setRequireVpnForUids(boolean, @NonNull java.util.Collection<android.util.Range<java.lang.Integer>>);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
@@ -40,9 +38,6 @@
     field public static final int BLOCKED_REASON_LOCKDOWN_VPN = 16; // 0x10
     field public static final int BLOCKED_REASON_NONE = 0; // 0x0
     field public static final int BLOCKED_REASON_RESTRICTED_MODE = 8; // 0x8
-    field public static final String PRIVATE_DNS_MODE_OFF = "off";
-    field public static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
-    field public static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname";
     field public static final int PROFILE_NETWORK_PREFERENCE_DEFAULT = 0; // 0x0
     field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1; // 0x1
   }
@@ -53,6 +48,7 @@
 
   public class ConnectivitySettingsManager {
     method public static void clearGlobalProxy(@NonNull android.content.Context);
+    method @NonNull public static java.util.Set<java.lang.String> getAppsAllowedOnRestrictedNetworks(@NonNull android.content.Context);
     method @Nullable public static String getCaptivePortalHttpUrl(@NonNull android.content.Context);
     method public static int getCaptivePortalMode(@NonNull android.content.Context, int);
     method @NonNull public static java.time.Duration getConnectivityKeepPendingIntentDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
@@ -62,15 +58,17 @@
     method @Nullable public static android.net.ProxyInfo getGlobalProxy(@NonNull android.content.Context);
     method @NonNull public static java.time.Duration getMobileDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
     method public static boolean getMobileDataAlwaysOn(@NonNull android.content.Context, boolean);
-    method @Nullable public static String getMobileDataPreferredApps(@NonNull android.content.Context);
+    method @NonNull public static java.util.Set<java.lang.Integer> getMobileDataPreferredUids(@NonNull android.content.Context);
     method public static int getNetworkAvoidBadWifi(@NonNull android.content.Context);
     method @Nullable public static String getNetworkMeteredMultipathPreference(@NonNull android.content.Context);
     method public static int getNetworkSwitchNotificationMaximumDailyCount(@NonNull android.content.Context, int);
     method @NonNull public static java.time.Duration getNetworkSwitchNotificationRateDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
     method @NonNull public static String getPrivateDnsDefaultMode(@NonNull android.content.Context);
     method @Nullable public static String getPrivateDnsHostname(@NonNull android.content.Context);
+    method public static int getPrivateDnsMode(@NonNull android.content.Context);
     method public static boolean getWifiAlwaysRequested(@NonNull android.content.Context, boolean);
     method @NonNull public static java.time.Duration getWifiDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
+    method public static void setAppsAllowedOnRestrictedNetworks(@NonNull android.content.Context, @NonNull java.util.Set<java.lang.String>);
     method public static void setCaptivePortalHttpUrl(@NonNull android.content.Context, @Nullable String);
     method public static void setCaptivePortalMode(@NonNull android.content.Context, int);
     method public static void setConnectivityKeepPendingIntentDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
@@ -80,13 +78,14 @@
     method public static void setGlobalProxy(@NonNull android.content.Context, @NonNull android.net.ProxyInfo);
     method public static void setMobileDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
     method public static void setMobileDataAlwaysOn(@NonNull android.content.Context, boolean);
-    method public static void setMobileDataPreferredApps(@NonNull android.content.Context, @Nullable String);
+    method public static void setMobileDataPreferredUids(@NonNull android.content.Context, @NonNull java.util.Set<java.lang.Integer>);
     method public static void setNetworkAvoidBadWifi(@NonNull android.content.Context, int);
     method public static void setNetworkMeteredMultipathPreference(@NonNull android.content.Context, @NonNull String);
     method public static void setNetworkSwitchNotificationMaximumDailyCount(@NonNull android.content.Context, @IntRange(from=0) int);
     method public static void setNetworkSwitchNotificationRateDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
-    method public static void setPrivateDnsDefaultMode(@NonNull android.content.Context, @NonNull String);
+    method public static void setPrivateDnsDefaultMode(@NonNull android.content.Context, @NonNull int);
     method public static void setPrivateDnsHostname(@NonNull android.content.Context, @Nullable String);
+    method public static void setPrivateDnsMode(@NonNull android.content.Context, int);
     method public static void setWifiAlwaysRequested(@NonNull android.content.Context, boolean);
     method public static void setWifiDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
     field public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; // 0x2
@@ -95,6 +94,9 @@
     field public static final int NETWORK_AVOID_BAD_WIFI_AVOID = 2; // 0x2
     field public static final int NETWORK_AVOID_BAD_WIFI_IGNORE = 0; // 0x0
     field public static final int NETWORK_AVOID_BAD_WIFI_PROMPT = 1; // 0x1
+    field public static final int PRIVATE_DNS_MODE_OFF = 1; // 0x1
+    field public static final int PRIVATE_DNS_MODE_OPPORTUNISTIC = 2; // 0x2
+    field public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = 3; // 0x3
   }
 
   public final class NetworkAgentConfig implements android.os.Parcelable {
@@ -109,7 +111,7 @@
 
   public final class NetworkCapabilities implements android.os.Parcelable {
     method @Nullable public java.util.Set<android.util.Range<java.lang.Integer>> getUids();
-    method public boolean hasUnwantedCapability(int);
+    method public boolean hasForbiddenCapability(int);
     field public static final long REDACT_ALL = -1L; // 0xffffffffffffffffL
     field public static final long REDACT_FOR_ACCESS_FINE_LOCATION = 1L; // 0x1L
     field public static final long REDACT_FOR_LOCAL_MAC_ADDRESS = 2L; // 0x2L
@@ -123,13 +125,13 @@
   }
 
   public class NetworkRequest implements android.os.Parcelable {
-    method @NonNull public int[] getUnwantedCapabilities();
-    method public boolean hasUnwantedCapability(int);
+    method @NonNull public int[] getForbiddenCapabilities();
+    method public boolean hasForbiddenCapability(int);
   }
 
   public static class NetworkRequest.Builder {
-    method @NonNull public android.net.NetworkRequest.Builder addUnwantedCapability(int);
-    method @NonNull public android.net.NetworkRequest.Builder removeUnwantedCapability(int);
+    method @NonNull public android.net.NetworkRequest.Builder addForbiddenCapability(int);
+    method @NonNull public android.net.NetworkRequest.Builder removeForbiddenCapability(int);
     method @NonNull public android.net.NetworkRequest.Builder setUids(@Nullable java.util.Set<android.util.Range<java.lang.Integer>>);
   }
 
diff --git a/core/jni/android_net_NetworkUtils.cpp b/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp
similarity index 93%
rename from core/jni/android_net_NetworkUtils.cpp
rename to packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp
index 1cee895..48e262a 100644
--- a/core/jni/android_net_NetworkUtils.cpp
+++ b/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp
@@ -30,13 +30,13 @@
 
 #include <DnsProxydProtocol.h> // NETID_USE_LOCAL_NAMESERVERS
 #include <cutils/properties.h>
+#include <nativehelper/JNIHelp.h>
 #include <nativehelper/JNIPlatformHelp.h>
 #include <nativehelper/ScopedLocalRef.h>
 #include <utils/Log.h>
 #include <utils/misc.h>
 
 #include "NetdClient.h"
-#include "core_jni_helpers.h"
 #include "jni.h"
 
 extern "C" {
@@ -52,6 +52,19 @@
 // FrameworkListener limits the size of commands to 4096 bytes.
 constexpr int MAXCMDSIZE = 4096;
 
+static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) {
+    jclass clazz = env->FindClass(class_name);
+    LOG_ALWAYS_FATAL_IF(clazz == NULL, "Unable to find class %s", class_name);
+    return clazz;
+}
+
+template <typename T>
+static inline T MakeGlobalRefOrDie(JNIEnv* env, T in) {
+    jobject res = env->NewGlobalRef(in);
+    LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to create global reference.");
+    return static_cast<T>(res);
+}
+
 static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
 {
     struct sock_filter filter_code[] = {
@@ -254,8 +267,8 @@
 
 int register_android_net_NetworkUtils(JNIEnv* env)
 {
-    return RegisterMethodsOrDie(env, NETUTILS_PKG_NAME, gNetworkUtilMethods,
-                                NELEM(gNetworkUtilMethods));
+    return jniRegisterNativeMethods(env, NETUTILS_PKG_NAME, gNetworkUtilMethods,
+                                    NELEM(gNetworkUtilMethods));
 }
 
 }; // namespace android
diff --git a/packages/Connectivity/framework/jni/onload.cpp b/packages/Connectivity/framework/jni/onload.cpp
new file mode 100644
index 0000000..435f434
--- /dev/null
+++ b/packages/Connectivity/framework/jni/onload.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#include <nativehelper/JNIHelp.h>
+#include <log/log.h>
+
+namespace android {
+
+int register_android_net_NetworkUtils(JNIEnv* env);
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
+    JNIEnv *env;
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        ALOGE("GetEnv failed");
+        return JNI_ERR;
+    }
+
+    if (register_android_net_NetworkUtils(env) < 0) {
+        return JNI_ERR;
+    }
+
+    return JNI_VERSION_1_6;
+}
+
+};
\ No newline at end of file
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 19764dd..1a6b37b 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -16,8 +16,6 @@
 package android.net;
 
 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
-import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_DEFAULT_MODE;
-import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE;
 import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST;
 import static android.net.NetworkRequest.Type.LISTEN;
 import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST;
@@ -33,7 +31,6 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.StringDef;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -41,7 +38,6 @@
 import android.app.admin.DevicePolicyManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.net.ConnectivityDiagnosticsManager.DataStallReport.DetectionMethod;
@@ -70,7 +66,6 @@
 import android.provider.Settings;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Range;
@@ -821,38 +816,6 @@
     public static final int NETID_UNSET = 0;
 
     /**
-     * Private DNS Mode values.
-     *
-     * The "private_dns_mode" global setting stores a String value which is
-     * expected to be one of the following.
-     */
-
-    /**
-     * @hide
-     */
-    @SystemApi(client = MODULE_LIBRARIES)
-    public static final String PRIVATE_DNS_MODE_OFF = "off";
-    /**
-     * @hide
-     */
-    @SystemApi(client = MODULE_LIBRARIES)
-    public static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
-    /**
-     * @hide
-     */
-    @SystemApi(client = MODULE_LIBRARIES)
-    public static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname";
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @StringDef(value = {
-            PRIVATE_DNS_MODE_OFF,
-            PRIVATE_DNS_MODE_OPPORTUNISTIC,
-            PRIVATE_DNS_MODE_PROVIDER_HOSTNAME,
-    })
-    public @interface PrivateDnsMode {}
-
-    /**
      * Flag to indicate that an app is not subject to any restrictions that could result in its
      * network access blocked.
      *
@@ -3374,7 +3337,60 @@
         provider.setProviderId(NetworkProvider.ID_NONE);
     }
 
+    /**
+     * Register or update a network offer with ConnectivityService.
+     *
+     * ConnectivityService keeps track of offers made by the various providers and matches
+     * them to networking requests made by apps or the system. The provider supplies a score
+     * and the capabilities of the network it might be able to bring up ; these act as filters
+     * used by ConnectivityService to only send those requests that can be fulfilled by the
+     * provider.
+     *
+     * The provider is under no obligation to be able to bring up the network it offers at any
+     * given time. Instead, this mechanism is meant to limit requests received by providers
+     * to those they actually have a chance to fulfill, as providers don't have a way to compare
+     * the quality of the network satisfying a given request to their own offer.
+     *
+     * An offer can be updated by calling this again with the same callback object. This is
+     * similar to calling unofferNetwork and offerNetwork again, but will only update the
+     * provider with the changes caused by the changes in the offer.
+     *
+     * @param provider The provider making this offer.
+     * @param score The prospective score of the network.
+     * @param caps The prospective capabilities of the network.
+     * @param callback The callback to call when this offer is needed or unneeded.
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_FACTORY})
+    public void offerNetwork(@NonNull final NetworkProvider provider,
+            @NonNull final NetworkScore score, @NonNull final NetworkCapabilities caps,
+            @NonNull final INetworkOfferCallback callback) {
+        try {
+            mService.offerNetwork(Objects.requireNonNull(provider.getMessenger(), "null messenger"),
+                    Objects.requireNonNull(score, "null score"),
+                    Objects.requireNonNull(caps, "null caps"),
+                    Objects.requireNonNull(callback, "null callback"));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 
+    /**
+     * Withdraw a network offer made with {@link #offerNetwork}.
+     *
+     * @param callback The callback passed at registration time. This must be the same object
+     *                 that was passed to {@link #offerNetwork}
+     * @hide
+     */
+    public void unofferNetwork(@NonNull final INetworkOfferCallback callback) {
+        try {
+            mService.unofferNetwork(Objects.requireNonNull(callback));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
     /** @hide exposed via the NetworkProvider class. */
     @RequiresPermission(anyOf = {
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
@@ -5448,44 +5464,4 @@
     public static Range<Integer> getIpSecNetIdRange() {
         return new Range(TUN_INTF_NETID_START, TUN_INTF_NETID_START + TUN_INTF_NETID_RANGE - 1);
     }
-
-    /**
-     * Get private DNS mode from settings.
-     *
-     * @param context The Context to query the private DNS mode from settings.
-     * @return A string of private DNS mode as one of the PRIVATE_DNS_MODE_* constants.
-     *
-     * @hide
-     */
-    @SystemApi(client = MODULE_LIBRARIES)
-    @NonNull
-    @PrivateDnsMode
-    public static String getPrivateDnsMode(@NonNull Context context) {
-        final ContentResolver cr = context.getContentResolver();
-        String mode = Settings.Global.getString(cr, PRIVATE_DNS_MODE);
-        if (TextUtils.isEmpty(mode)) mode = Settings.Global.getString(cr, PRIVATE_DNS_DEFAULT_MODE);
-        // If both PRIVATE_DNS_MODE and PRIVATE_DNS_DEFAULT_MODE are not set, choose
-        // PRIVATE_DNS_MODE_OPPORTUNISTIC as default mode.
-        if (TextUtils.isEmpty(mode)) mode = PRIVATE_DNS_MODE_OPPORTUNISTIC;
-        return mode;
-    }
-
-    /**
-     * Set private DNS mode to settings.
-     *
-     * @param context The {@link Context} to set the private DNS mode.
-     * @param mode The private dns mode. This should be one of the PRIVATE_DNS_MODE_* constants.
-     *
-     * @hide
-     */
-    @SystemApi(client = MODULE_LIBRARIES)
-    public static void setPrivateDnsMode(@NonNull Context context,
-            @NonNull @PrivateDnsMode String mode) {
-        if (!(mode == PRIVATE_DNS_MODE_OFF
-                || mode == PRIVATE_DNS_MODE_OPPORTUNISTIC
-                || mode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) {
-            throw new IllegalArgumentException("Invalid private dns mode");
-        }
-        Settings.Global.putString(context.getContentResolver(), PRIVATE_DNS_MODE, mode);
-    }
 }
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java
index 9a00055..762f24f 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java
@@ -19,20 +19,20 @@
 import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER;
 import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE;
 import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY;
-import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
-import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
-import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
 
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.net.ConnectivityManager.MultipathPreference;
-import android.net.ConnectivityManager.PrivateDnsMode;
+import android.os.Process;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Range;
 
 import com.android.net.module.util.ProxyUtils;
@@ -41,6 +41,8 @@
 import java.lang.annotation.RetentionPolicy;
 import java.time.Duration;
 import java.util.List;
+import java.util.Set;
+import java.util.StringJoiner;
 
 /**
  * A manager class for connectivity module settings.
@@ -333,12 +335,51 @@
             "network_metered_multipath_preference";
 
     /**
-     * A list of apps that should go on cellular networks in preference even when higher-priority
+     * A list of uids that should go on cellular networks in preference even when higher-priority
      * networks are connected.
      *
      * @hide
      */
-    public static final String MOBILE_DATA_PREFERRED_APPS = "mobile_data_preferred_apps";
+    public static final String MOBILE_DATA_PREFERRED_UIDS = "mobile_data_preferred_uids";
+
+    /**
+     * One of the private DNS modes that indicates the private DNS mode is off.
+     */
+    public static final int PRIVATE_DNS_MODE_OFF = 1;
+
+    /**
+     * One of the private DNS modes that indicates the private DNS mode is automatic, which
+     * will try to use the current DNS as private DNS.
+     */
+    public static final int PRIVATE_DNS_MODE_OPPORTUNISTIC = 2;
+
+    /**
+     * One of the private DNS modes that indicates the private DNS mode is strict and the
+     * {@link #PRIVATE_DNS_SPECIFIER} is required, which will try to use the value of
+     * {@link #PRIVATE_DNS_SPECIFIER} as private DNS.
+     */
+    public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = 3;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            PRIVATE_DNS_MODE_OFF,
+            PRIVATE_DNS_MODE_OPPORTUNISTIC,
+            PRIVATE_DNS_MODE_PROVIDER_HOSTNAME,
+    })
+    public @interface PrivateDnsMode {}
+
+    private static final String PRIVATE_DNS_MODE_OFF_STRING = "off";
+    private static final String PRIVATE_DNS_MODE_OPPORTUNISTIC_STRING = "opportunistic";
+    private static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME_STRING = "hostname";
+
+    /**
+     * A list of apps that is allowed on restricted networks.
+     *
+     * @hide
+     */
+    public static final String APPS_ALLOWED_ON_RESTRICTED_NETWORKS =
+            "apps_allowed_on_restricted_networks";
 
     /**
      * Get mobile data activity timeout from {@link Settings}.
@@ -689,6 +730,65 @@
                 context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, "" /* value */);
     }
 
+    private static String getPrivateDnsModeAsString(@PrivateDnsMode int mode) {
+        switch (mode) {
+            case PRIVATE_DNS_MODE_OFF:
+                return PRIVATE_DNS_MODE_OFF_STRING;
+            case PRIVATE_DNS_MODE_OPPORTUNISTIC:
+                return PRIVATE_DNS_MODE_OPPORTUNISTIC_STRING;
+            case PRIVATE_DNS_MODE_PROVIDER_HOSTNAME:
+                return PRIVATE_DNS_MODE_PROVIDER_HOSTNAME_STRING;
+            default:
+                throw new IllegalArgumentException("Invalid private dns mode: " + mode);
+        }
+    }
+
+    private static int getPrivateDnsModeAsInt(String mode) {
+        switch (mode) {
+            case "off":
+                return PRIVATE_DNS_MODE_OFF;
+            case "hostname":
+                return PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+            case "opportunistic":
+                return PRIVATE_DNS_MODE_OPPORTUNISTIC;
+            default:
+                throw new IllegalArgumentException("Invalid private dns mode: " + mode);
+        }
+    }
+
+    /**
+     * Get private DNS mode from settings.
+     *
+     * @param context The Context to query the private DNS mode from settings.
+     * @return A string of private DNS mode.
+     */
+    @PrivateDnsMode
+    public static int getPrivateDnsMode(@NonNull Context context) {
+        final ContentResolver cr = context.getContentResolver();
+        String mode = Settings.Global.getString(cr, PRIVATE_DNS_MODE);
+        if (TextUtils.isEmpty(mode)) mode = Settings.Global.getString(cr, PRIVATE_DNS_DEFAULT_MODE);
+        // If both PRIVATE_DNS_MODE and PRIVATE_DNS_DEFAULT_MODE are not set, choose
+        // PRIVATE_DNS_MODE_OPPORTUNISTIC as default mode.
+        if (TextUtils.isEmpty(mode)) return PRIVATE_DNS_MODE_OPPORTUNISTIC;
+        return getPrivateDnsModeAsInt(mode);
+    }
+
+    /**
+     * Set private DNS mode to settings.
+     *
+     * @param context The {@link Context} to set the private DNS mode.
+     * @param mode The private dns mode. This should be one of the PRIVATE_DNS_MODE_* constants.
+     */
+    public static void setPrivateDnsMode(@NonNull Context context, @PrivateDnsMode int mode) {
+        if (!(mode == PRIVATE_DNS_MODE_OFF
+                || mode == PRIVATE_DNS_MODE_OPPORTUNISTIC
+                || mode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) {
+            throw new IllegalArgumentException("Invalid private dns mode: " + mode);
+        }
+        Settings.Global.putString(context.getContentResolver(), PRIVATE_DNS_MODE,
+                getPrivateDnsModeAsString(mode));
+    }
+
     /**
      * Get specific private dns provider name from {@link Settings}.
      *
@@ -731,13 +831,14 @@
      *             constants.
      */
     public static void setPrivateDnsDefaultMode(@NonNull Context context,
-            @NonNull @PrivateDnsMode String mode) {
+            @NonNull @PrivateDnsMode int mode) {
         if (!(mode == PRIVATE_DNS_MODE_OFF
                 || mode == PRIVATE_DNS_MODE_OPPORTUNISTIC
                 || mode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) {
             throw new IllegalArgumentException("Invalid private dns mode");
         }
-        Settings.Global.putString(context.getContentResolver(), PRIVATE_DNS_DEFAULT_MODE, mode);
+        Settings.Global.putString(context.getContentResolver(), PRIVATE_DNS_DEFAULT_MODE,
+                getPrivateDnsModeAsString(mode));
     }
 
     /**
@@ -903,27 +1004,84 @@
     }
 
     /**
-     * Get the list of apps(from {@link Settings}) that should go on cellular networks in preference
+     * Get the list of uids(from {@link Settings}) that should go on cellular networks in preference
      * even when higher-priority networks are connected.
      *
      * @param context The {@link Context} to query the setting.
-     * @return A list of apps that should go on cellular networks in preference even when
+     * @return A list of uids that should go on cellular networks in preference even when
      *         higher-priority networks are connected or null if no setting value.
      */
-    @Nullable
-    public static String getMobileDataPreferredApps(@NonNull Context context) {
-        return Settings.Secure.getString(context.getContentResolver(), MOBILE_DATA_PREFERRED_APPS);
+    @NonNull
+    public static Set<Integer> getMobileDataPreferredUids(@NonNull Context context) {
+        final String uidList = Settings.Secure.getString(
+                context.getContentResolver(), MOBILE_DATA_PREFERRED_UIDS);
+        final Set<Integer> uids = new ArraySet<>();
+        if (TextUtils.isEmpty(uidList)) {
+            return uids;
+        }
+        for (String uid : uidList.split(";")) {
+            uids.add(Integer.valueOf(uid));
+        }
+        return uids;
     }
 
     /**
-     * Set the list of apps(to {@link Settings}) that should go on cellular networks in preference
+     * Set the list of uids(to {@link Settings}) that should go on cellular networks in preference
      * even when higher-priority networks are connected.
      *
      * @param context The {@link Context} to set the setting.
-     * @param list A list of apps that should go on cellular networks in preference even when
+     * @param uidList A list of uids that should go on cellular networks in preference even when
      *             higher-priority networks are connected.
      */
-    public static void setMobileDataPreferredApps(@NonNull Context context, @Nullable String list) {
-        Settings.Secure.putString(context.getContentResolver(), MOBILE_DATA_PREFERRED_APPS, list);
+    public static void setMobileDataPreferredUids(@NonNull Context context,
+            @NonNull Set<Integer> uidList) {
+        final StringJoiner joiner = new StringJoiner(";");
+        for (Integer uid : uidList) {
+            if (uid < 0 || UserHandle.getAppId(uid) > Process.LAST_APPLICATION_UID) {
+                throw new IllegalArgumentException("Invalid uid");
+            }
+            joiner.add(uid.toString());
+        }
+        Settings.Secure.putString(
+                context.getContentResolver(), MOBILE_DATA_PREFERRED_UIDS, joiner.toString());
+    }
+
+    /**
+     * Get the list of apps(from {@link Settings}) that is allowed on restricted networks.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @return A list of apps that is allowed on restricted networks or null if no setting
+     *         value.
+     */
+    @NonNull
+    public static Set<String> getAppsAllowedOnRestrictedNetworks(@NonNull Context context) {
+        final String appList = Settings.Secure.getString(
+                context.getContentResolver(), APPS_ALLOWED_ON_RESTRICTED_NETWORKS);
+        if (TextUtils.isEmpty(appList)) {
+            return new ArraySet<>();
+        }
+        return new ArraySet<>(appList.split(";"));
+    }
+
+    /**
+     * Set the list of apps(from {@link Settings}) that is allowed on restricted networks.
+     *
+     * Note: Please refer to android developer guidelines for valid app(package name).
+     * https://developer.android.com/guide/topics/manifest/manifest-element.html#package
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param list A list of apps that is allowed on restricted networks.
+     */
+    public static void setAppsAllowedOnRestrictedNetworks(@NonNull Context context,
+            @NonNull Set<String> list) {
+        final StringJoiner joiner = new StringJoiner(";");
+        for (String app : list) {
+            if (app == null || app.contains(";")) {
+                throw new IllegalArgumentException("Invalid app(package name)");
+            }
+            joiner.add(app);
+        }
+        Settings.Secure.putString(context.getContentResolver(), APPS_ALLOWED_ON_RESTRICTED_NETWORKS,
+                joiner.toString());
     }
 }
diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index a7cb618f..d937c9cd 100644
--- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -23,6 +23,7 @@
 import android.net.INetworkAgent;
 import android.net.IOnCompleteListener;
 import android.net.INetworkActivityListener;
+import android.net.INetworkOfferCallback;
 import android.net.IQosCallback;
 import android.net.ISocketKeepaliveCallback;
 import android.net.LinkProperties;
@@ -221,4 +222,8 @@
             in IOnCompleteListener listener);
 
     int getRestrictBackgroundStatusByCaller();
+
+    void offerNetwork(in Messenger messenger, in NetworkScore score,
+            in NetworkCapabilities caps, in INetworkOfferCallback callback);
+    void unofferNetwork(in INetworkOfferCallback callback);
 }
diff --git a/packages/Connectivity/framework/src/android/net/INetworkOfferCallback.aidl b/packages/Connectivity/framework/src/android/net/INetworkOfferCallback.aidl
new file mode 100644
index 0000000..67d2d405
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/INetworkOfferCallback.aidl
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 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;
+
+import android.net.NetworkRequest;
+
+/**
+ * A callback registered with connectivity by network providers together with
+ * a NetworkOffer.
+ *
+ * When the offer is needed to satisfy some application or system component,
+ * connectivity will call onOfferNeeded on this callback. When this happens,
+ * the provider should try and bring up the network.
+ *
+ * When the offer is no longer needed, for example because the application has
+ * withdrawn the request or if the request is being satisfied by a network
+ * that this offer will never be able to beat, connectivity calls
+ * onOfferUnneeded. When this happens, the provider should stop trying to
+ * bring up the network, or tear it down if it has already been brought up.
+ *
+ * When NetworkProvider#offerNetwork is called, the provider can expect to
+ * immediately receive all requests that can be fulfilled by that offer and
+ * are not already satisfied by a better network. It is possible no such
+ * request is currently outstanding, because no requests have been made that
+ * can be satisfied by this offer, or because all such requests are already
+ * satisfied by a better network.
+ * onOfferNeeded can be called at any time after registration and until the
+ * offer is withdrawn with NetworkProvider#unofferNetwork is called. This
+ * typically happens when a new network request is filed by an application,
+ * or when the network satisfying a request disconnects and this offer now
+ * stands a chance to be the best network for it.
+ *
+ * @hide
+ */
+oneway interface INetworkOfferCallback {
+    /**
+     * Informs the registrant that the offer is needed to fulfill this request.
+     * @param networkRequest the request to satisfy
+     * @param providerId the ID of the provider currently satisfying
+     *          this request, or NetworkProvider.ID_NONE if none.
+     */
+    void onOfferNeeded(in NetworkRequest networkRequest, int providerId);
+
+    /**
+     * Informs the registrant that the offer is no longer needed to fulfill this request.
+     */
+    void onOfferUnneeded(in NetworkRequest networkRequest);
+}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 950b1c7..c19a906 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -139,19 +139,13 @@
      */
     private String mRequestorPackageName;
 
-    /**
-     * Indicates what fields should be redacted from this instance.
-     */
-    private final @RedactionType long mRedactions;
-
     public NetworkCapabilities() {
-        mRedactions = REDACT_ALL;
         clearAll();
         mNetworkCapabilities = DEFAULT_CAPABILITIES;
     }
 
     public NetworkCapabilities(NetworkCapabilities nc) {
-        this(nc, REDACT_ALL);
+        this(nc, REDACT_NONE);
     }
 
     /**
@@ -163,10 +157,12 @@
      * @hide
      */
     public NetworkCapabilities(@Nullable NetworkCapabilities nc, @RedactionType long redactions) {
-        mRedactions = redactions;
         if (nc != null) {
             set(nc);
         }
+        if (mTransportInfo != null) {
+            mTransportInfo = nc.mTransportInfo.makeCopy(redactions);
+        }
     }
 
     /**
@@ -175,15 +171,7 @@
      * @hide
      */
     public void clearAll() {
-        // Ensures that the internal copies maintained by the connectivity stack does not set it to
-        // anything other than |REDACT_ALL|.
-        if (mRedactions != REDACT_ALL) {
-            // This is needed because the current redaction mechanism relies on redaction while
-            // parceling.
-            throw new UnsupportedOperationException(
-                    "Cannot clear NetworkCapabilities when mRedactions is set");
-        }
-        mNetworkCapabilities = mTransportTypes = mUnwantedNetworkCapabilities = 0;
+        mNetworkCapabilities = mTransportTypes = mForbiddenNetworkCapabilities = 0;
         mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
         mNetworkSpecifier = null;
         mTransportInfo = null;
@@ -211,7 +199,7 @@
         mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
         mNetworkSpecifier = nc.mNetworkSpecifier;
         if (nc.getTransportInfo() != null) {
-            setTransportInfo(nc.getTransportInfo().makeCopy(mRedactions));
+            setTransportInfo(nc.getTransportInfo());
         } else {
             setTransportInfo(null);
         }
@@ -219,7 +207,7 @@
         mUids = (nc.mUids == null) ? null : new ArraySet<>(nc.mUids);
         setAdministratorUids(nc.getAdministratorUids());
         mOwnerUid = nc.mOwnerUid;
-        mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities;
+        mForbiddenNetworkCapabilities = nc.mForbiddenNetworkCapabilities;
         mSSID = nc.mSSID;
         mPrivateDnsBroken = nc.mPrivateDnsBroken;
         mRequestorUid = nc.mRequestorUid;
@@ -237,7 +225,7 @@
     /**
      * If any capabilities specified here they must not exist in the matching Network.
      */
-    private long mUnwantedNetworkCapabilities;
+    private long mForbiddenNetworkCapabilities;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -586,21 +574,21 @@
      * @hide
      */
     public @NonNull NetworkCapabilities addCapability(@NetCapability int capability) {
-        // If the given capability was previously added to the list of unwanted capabilities
-        // then the capability will also be removed from the list of unwanted capabilities.
-        // TODO: Consider adding unwanted capabilities to the public API and mention this
+        // If the given capability was previously added to the list of forbidden capabilities
+        // then the capability will also be removed from the list of forbidden capabilities.
+        // TODO: Consider adding forbidden capabilities to the public API and mention this
         // in the documentation.
         checkValidCapability(capability);
         mNetworkCapabilities |= 1L << capability;
-        // remove from unwanted capability list
-        mUnwantedNetworkCapabilities &= ~(1L << capability);
+        // remove from forbidden capability list
+        mForbiddenNetworkCapabilities &= ~(1L << capability);
         return this;
     }
 
     /**
-     * Adds the given capability to the list of unwanted capabilities of this
+     * Adds the given capability to the list of forbidden capabilities of this
      * {@code NetworkCapability} instance. Note that when searching for a network to
-     * satisfy a request, the network must not contain any capability from unwanted capability
+     * satisfy a request, the network must not contain any capability from forbidden capability
      * list.
      * <p>
      * If the capability was previously added to the list of required capabilities (for
@@ -610,9 +598,9 @@
      * @see #addCapability(int)
      * @hide
      */
-    public void addUnwantedCapability(@NetCapability int capability) {
+    public void addForbiddenCapability(@NetCapability int capability) {
         checkValidCapability(capability);
-        mUnwantedNetworkCapabilities |= 1L << capability;
+        mForbiddenNetworkCapabilities |= 1L << capability;
         mNetworkCapabilities &= ~(1L << capability);  // remove from requested capabilities
     }
 
@@ -632,16 +620,16 @@
     }
 
     /**
-     * Removes (if found) the given unwanted capability from this {@code NetworkCapability}
-     * instance that were added via addUnwantedCapability(int) or setCapabilities(int[], int[]).
+     * Removes (if found) the given forbidden capability from this {@code NetworkCapability}
+     * instance that were added via addForbiddenCapability(int) or setCapabilities(int[], int[]).
      *
      * @param capability the capability to be removed.
      * @return This NetworkCapabilities instance, to facilitate chaining.
      * @hide
      */
-    public @NonNull NetworkCapabilities removeUnwantedCapability(@NetCapability int capability) {
+    public @NonNull NetworkCapabilities removeForbiddenCapability(@NetCapability int capability) {
         checkValidCapability(capability);
-        mUnwantedNetworkCapabilities &= ~(1L << capability);
+        mForbiddenNetworkCapabilities &= ~(1L << capability);
         return this;
     }
 
@@ -670,13 +658,13 @@
     }
 
     /**
-     * Gets all the unwanted capabilities set on this {@code NetworkCapability} instance.
+     * Gets all the forbidden capabilities set on this {@code NetworkCapability} instance.
      *
-     * @return an array of unwanted capability values for this instance.
+     * @return an array of forbidden capability values for this instance.
      * @hide
      */
-    public @NetCapability int[] getUnwantedCapabilities() {
-        return NetworkCapabilitiesUtils.unpackBits(mUnwantedNetworkCapabilities);
+    public @NetCapability int[] getForbiddenCapabilities() {
+        return NetworkCapabilitiesUtils.unpackBits(mForbiddenNetworkCapabilities);
     }
 
 
@@ -687,9 +675,9 @@
      * @hide
      */
     public void setCapabilities(@NetCapability int[] capabilities,
-            @NetCapability int[] unwantedCapabilities) {
+            @NetCapability int[] forbiddenCapabilities) {
         mNetworkCapabilities = NetworkCapabilitiesUtils.packBits(capabilities);
-        mUnwantedNetworkCapabilities = NetworkCapabilitiesUtils.packBits(unwantedCapabilities);
+        mForbiddenNetworkCapabilities = NetworkCapabilitiesUtils.packBits(forbiddenCapabilities);
     }
 
     /**
@@ -714,9 +702,9 @@
 
     /** @hide */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-    public boolean hasUnwantedCapability(@NetCapability int capability) {
+    public boolean hasForbiddenCapability(@NetCapability int capability) {
         return isValidCapability(capability)
-                && ((mUnwantedNetworkCapabilities & (1L << capability)) != 0);
+                && ((mForbiddenNetworkCapabilities & (1L << capability)) != 0);
     }
 
     /**
@@ -746,14 +734,14 @@
 
     private void combineNetCapabilities(@NonNull NetworkCapabilities nc) {
         final long wantedCaps = this.mNetworkCapabilities | nc.mNetworkCapabilities;
-        final long unwantedCaps =
-                this.mUnwantedNetworkCapabilities | nc.mUnwantedNetworkCapabilities;
-        if ((wantedCaps & unwantedCaps) != 0) {
+        final long forbiddenCaps =
+                this.mForbiddenNetworkCapabilities | nc.mForbiddenNetworkCapabilities;
+        if ((wantedCaps & forbiddenCaps) != 0) {
             throw new IllegalArgumentException(
-                    "Cannot have the same capability in wanted and unwanted lists.");
+                    "Cannot have the same capability in wanted and forbidden lists.");
         }
         this.mNetworkCapabilities = wantedCaps;
-        this.mUnwantedNetworkCapabilities = unwantedCaps;
+        this.mForbiddenNetworkCapabilities = forbiddenCaps;
     }
 
     /**
@@ -764,7 +752,7 @@
      * @hide
      */
     public @Nullable String describeFirstNonRequestableCapability() {
-        final long nonRequestable = (mNetworkCapabilities | mUnwantedNetworkCapabilities)
+        final long nonRequestable = (mNetworkCapabilities | mForbiddenNetworkCapabilities)
                 & NON_REQUESTABLE_CAPABILITIES;
 
         if (nonRequestable != 0) {
@@ -781,28 +769,28 @@
     private boolean satisfiedByNetCapabilities(@NonNull NetworkCapabilities nc,
             boolean onlyImmutable) {
         long requestedCapabilities = mNetworkCapabilities;
-        long requestedUnwantedCapabilities = mUnwantedNetworkCapabilities;
+        long requestedForbiddenCapabilities = mForbiddenNetworkCapabilities;
         long providedCapabilities = nc.mNetworkCapabilities;
 
         if (onlyImmutable) {
             requestedCapabilities &= ~MUTABLE_CAPABILITIES;
-            requestedUnwantedCapabilities &= ~MUTABLE_CAPABILITIES;
+            requestedForbiddenCapabilities &= ~MUTABLE_CAPABILITIES;
         }
         return ((providedCapabilities & requestedCapabilities) == requestedCapabilities)
-                && ((requestedUnwantedCapabilities & providedCapabilities) == 0);
+                && ((requestedForbiddenCapabilities & providedCapabilities) == 0);
     }
 
     /** @hide */
     public boolean equalsNetCapabilities(@NonNull NetworkCapabilities nc) {
         return (nc.mNetworkCapabilities == this.mNetworkCapabilities)
-                && (nc.mUnwantedNetworkCapabilities == this.mUnwantedNetworkCapabilities);
+                && (nc.mForbiddenNetworkCapabilities == this.mForbiddenNetworkCapabilities);
     }
 
     private boolean equalsNetCapabilitiesRequestable(@NonNull NetworkCapabilities that) {
-        return ((this.mNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES) ==
-                (that.mNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES))
-                && ((this.mUnwantedNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES) ==
-                (that.mUnwantedNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES));
+        return ((this.mNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES)
+                == (that.mNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES))
+                && ((this.mForbiddenNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES)
+                == (that.mForbiddenNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES));
     }
 
     /**
@@ -830,8 +818,17 @@
         final int[] originalAdministratorUids = getAdministratorUids();
         final TransportInfo originalTransportInfo = getTransportInfo();
         clearAll();
-        mTransportTypes = (originalTransportTypes & TEST_NETWORKS_ALLOWED_TRANSPORTS)
-                | (1 << TRANSPORT_TEST);
+        if (0 != (originalCapabilities & NET_CAPABILITY_NOT_RESTRICTED)) {
+            // If the test network is not restricted, then it is only allowed to declare some
+            // specific transports. This is to minimize impact on running apps in case an app
+            // run from the shell creates a test a network.
+            mTransportTypes =
+                    (originalTransportTypes & UNRESTRICTED_TEST_NETWORKS_ALLOWED_TRANSPORTS)
+                            | (1 << TRANSPORT_TEST);
+        } else {
+            // If the test transport is restricted, then it may declare any transport.
+            mTransportTypes = (originalTransportTypes | (1 << TRANSPORT_TEST));
+        }
         mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES;
         mNetworkSpecifier = originalSpecifier;
         mSignalStrength = originalSignalStrength;
@@ -935,9 +932,10 @@
     };
 
     /**
-     * Allowed transports on a test network, in addition to TRANSPORT_TEST.
+     * Allowed transports on an unrestricted test network (in addition to TRANSPORT_TEST).
      */
-    private static final int TEST_NETWORKS_ALLOWED_TRANSPORTS = 1 << TRANSPORT_TEST
+    private static final int UNRESTRICTED_TEST_NETWORKS_ALLOWED_TRANSPORTS =
+            1 << TRANSPORT_TEST
             // Test ethernet networks can be created with EthernetManager#setIncludeTestInterfaces
             | 1 << TRANSPORT_ETHERNET
             // Test VPN networks can be created but their UID ranges must be empty.
@@ -1718,7 +1716,7 @@
      * Combine a set of Capabilities to this one.  Useful for coming up with the complete set.
      * <p>
      * Note that this method may break an invariant of having a particular capability in either
-     * wanted or unwanted lists but never in both.  Requests that have the same capability in
+     * wanted or forbidden lists but never in both.  Requests that have the same capability in
      * both lists will never be satisfied.
      * @hide
      */
@@ -1859,8 +1857,8 @@
     public int hashCode() {
         return (int) (mNetworkCapabilities & 0xFFFFFFFF)
                 + ((int) (mNetworkCapabilities >> 32) * 3)
-                + ((int) (mUnwantedNetworkCapabilities & 0xFFFFFFFF) * 5)
-                + ((int) (mUnwantedNetworkCapabilities >> 32) * 7)
+                + ((int) (mForbiddenNetworkCapabilities & 0xFFFFFFFF) * 5)
+                + ((int) (mForbiddenNetworkCapabilities >> 32) * 7)
                 + ((int) (mTransportTypes & 0xFFFFFFFF) * 11)
                 + ((int) (mTransportTypes >> 32) * 13)
                 + mLinkUpBandwidthKbps * 17
@@ -1895,7 +1893,7 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeLong(mNetworkCapabilities);
-        dest.writeLong(mUnwantedNetworkCapabilities);
+        dest.writeLong(mForbiddenNetworkCapabilities);
         dest.writeLong(mTransportTypes);
         dest.writeInt(mLinkUpBandwidthKbps);
         dest.writeInt(mLinkDownBandwidthKbps);
@@ -1919,7 +1917,7 @@
                 NetworkCapabilities netCap = new NetworkCapabilities();
 
                 netCap.mNetworkCapabilities = in.readLong();
-                netCap.mUnwantedNetworkCapabilities = in.readLong();
+                netCap.mForbiddenNetworkCapabilities = in.readLong();
                 netCap.mTransportTypes = in.readLong();
                 netCap.mLinkUpBandwidthKbps = in.readInt();
                 netCap.mLinkDownBandwidthKbps = in.readInt();
@@ -1973,9 +1971,9 @@
             appendStringRepresentationOfBitMaskToStringBuilder(sb, mNetworkCapabilities,
                     NetworkCapabilities::capabilityNameOf, "&");
         }
-        if (0 != mUnwantedNetworkCapabilities) {
-            sb.append(" Unwanted: ");
-            appendStringRepresentationOfBitMaskToStringBuilder(sb, mUnwantedNetworkCapabilities,
+        if (0 != mForbiddenNetworkCapabilities) {
+            sb.append(" Forbidden: ");
+            appendStringRepresentationOfBitMaskToStringBuilder(sb, mForbiddenNetworkCapabilities,
                     NetworkCapabilities::capabilityNameOf, "&");
         }
         if (mLinkUpBandwidthKbps > 0) {
diff --git a/packages/Connectivity/framework/src/android/net/NetworkProvider.java b/packages/Connectivity/framework/src/android/net/NetworkProvider.java
index 14cb51c..8f93047 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkProvider.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkProvider.java
@@ -28,6 +28,11 @@
 import android.os.Messenger;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.concurrent.Executor;
+
 /**
  * Base class for network providers such as telephony or Wi-Fi. NetworkProviders connect the device
  * to networks and makes them available to the core network stack by creating
@@ -78,7 +83,9 @@
      */
     @SystemApi
     public NetworkProvider(@NonNull Context context, @NonNull Looper looper, @NonNull String name) {
-        Handler handler = new Handler(looper) {
+        // TODO (b/174636568) : this class should be able to cache an instance of
+        // ConnectivityManager so it doesn't have to fetch it again every time.
+        final Handler handler = new Handler(looper) {
             @Override
             public void handleMessage(Message m) {
                 switch (m.what) {
@@ -159,4 +166,148 @@
     public void declareNetworkRequestUnfulfillable(@NonNull NetworkRequest request) {
         ConnectivityManager.from(mContext).declareNetworkRequestUnfulfillable(request);
     }
+
+    /** @hide */
+    // TODO : make @SystemApi when the impl is complete
+    public interface NetworkOfferCallback {
+        /** Called by the system when this offer is needed to satisfy some networking request. */
+        void onOfferNeeded(@NonNull NetworkRequest request, int providerId);
+        /** Called by the system when this offer is no longer needed. */
+        void onOfferUnneeded(@NonNull NetworkRequest request);
+    }
+
+    private class NetworkOfferCallbackProxy extends INetworkOfferCallback.Stub {
+        @NonNull public final NetworkOfferCallback callback;
+        @NonNull private final Executor mExecutor;
+
+        NetworkOfferCallbackProxy(@NonNull final NetworkOfferCallback callback,
+                @NonNull final Executor executor) {
+            this.callback = callback;
+            this.mExecutor = executor;
+        }
+
+        @Override
+        public void onOfferNeeded(final @NonNull NetworkRequest request,
+                final int providerId) {
+            mExecutor.execute(() -> callback.onOfferNeeded(request, providerId));
+        }
+
+        @Override
+        public void onOfferUnneeded(final @NonNull NetworkRequest request) {
+            mExecutor.execute(() -> callback.onOfferUnneeded(request));
+        }
+    }
+
+    @GuardedBy("mProxies")
+    @NonNull private final ArrayList<NetworkOfferCallbackProxy> mProxies = new ArrayList<>();
+
+    // Returns the proxy associated with this callback, or null if none.
+    @Nullable
+    private NetworkOfferCallbackProxy findProxyForCallback(@NonNull final NetworkOfferCallback cb) {
+        synchronized (mProxies) {
+            for (final NetworkOfferCallbackProxy p : mProxies) {
+                if (p.callback == cb) return p;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Register or update an offer for network with the passed caps and score.
+     *
+     * A NetworkProvider's job is to provide networks. This function is how a provider tells the
+     * connectivity stack what kind of network it may provide. The score and caps arguments act
+     * as filters that the connectivity stack uses to tell when the offer is necessary. When an
+     * offer might be advantageous over existing networks, the provider will receive a call to
+     * the associated callback's {@link NetworkOfferCallback#onOfferNeeded} method. The provider
+     * should then try to bring up this network. When an offer is no longer needed, the stack
+     * will inform the provider by calling {@link NetworkOfferCallback#onOfferUnneeded}. The
+     * provider should stop trying to bring up such a network, or disconnect it if it already has
+     * one.
+     *
+     * The stack determines what offers are needed according to what networks are currently
+     * available to the system, and what networking requests are made by applications. If an
+     * offer looks like it could be a better choice than any existing network for any particular
+     * request, that's when the stack decides the offer is needed. If the current networking
+     * requests are all satisfied by networks that this offer can't possibly be a better match
+     * for, that's when the offer is unneeded. An offer starts off as unneeded ; the provider
+     * should not try to bring up the network until {@link NetworkOfferCallback#onOfferNeeded}
+     * is called.
+     *
+     * Note that the offers are non-binding to the providers, in particular because providers
+     * often don't know if they will be able to bring up such a network at any given time. For
+     * example, no wireless network may be in range when the offer is needed. This is fine and
+     * expected ; the provider should simply continue to try to bring up the network and do so
+     * if/when it becomes possible. In the mean time, the stack will continue to satisfy requests
+     * with the best network currently available, or if none, keep the apps informed that no
+     * network can currently satisfy this request. When/if the provider can bring up the network,
+     * the connectivity stack will match it against requests, and inform interested apps of the
+     * availability of this network. This may, in turn, render the offer of some other provider
+     * unneeded if all requests it used to satisfy are now better served by this network.
+     *
+     * A network can become unneeded for a reason like the above : whether the provider managed
+     * to bring up the offered network after it became needed or not, some other provider may
+     * bring up a better network than this one, making this offer unneeded. A network may also
+     * become unneeded if the application making the request withdrew it (for example, after it
+     * is done transferring data, or if the user canceled an operation).
+     *
+     * The capabilities and score act as filters as to what requests the provider will see.
+     * They are not promises, but for best performance, the providers should strive to put
+     * as much known information as possible in the offer. For capabilities in particular, it
+     * should put all NetworkAgent-managed capabilities a network may have, even if it doesn't
+     * have them at first. This applies to INTERNET, for example ; if a provider thinks the
+     * network it can bring up for this offer may offer Internet access it should include the
+     * INTERNET bit. It's fine if the brought up network ends up not actually having INTERNET.
+     *
+     * TODO : in the future, to avoid possible infinite loops, there should be constraints on
+     * what can be put in capabilities of networks brought up for an offer. If a provider might
+     * bring up a network with or without INTERNET, then it should file two offers : this will
+     * let it know precisely what networks are needed, so it can avoid bringing up networks that
+     * won't actually satisfy requests and remove the risk for bring-up-bring-down loops.
+     *
+     * @hide
+     */
+    // TODO : make @SystemApi when the impl is complete
+    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+    public void offerNetwork(@NonNull final NetworkScore score,
+            @NonNull final NetworkCapabilities caps, @NonNull final Executor executor,
+            @NonNull final NetworkOfferCallback callback) {
+        NetworkOfferCallbackProxy proxy = null;
+        synchronized (mProxies) {
+            for (final NetworkOfferCallbackProxy existingProxy : mProxies) {
+                if (existingProxy.callback == callback) {
+                    proxy = existingProxy;
+                    break;
+                }
+            }
+            if (null == proxy) {
+                proxy = new NetworkOfferCallbackProxy(callback, executor);
+                mProxies.add(proxy);
+            }
+        }
+        mContext.getSystemService(ConnectivityManager.class).offerNetwork(this, score, caps, proxy);
+    }
+
+    /**
+     * Withdraw a network offer previously made to the networking stack.
+     *
+     * If a provider can no longer provide a network they offered, it should call this method.
+     * An example of usage could be if the hardware necessary to bring up the network was turned
+     * off in UI by the user. Note that because offers are never binding, the provider might
+     * alternatively decide not to withdraw this offer and simply refuse to bring up the network
+     * even when it's needed. However, withdrawing the request is slightly more resource-efficient
+     * because the networking stack won't have to compare this offer to exiting networks to see
+     * if it could beat any of them, and may be advantageous to the provider's implementation that
+     * can rely on no longer receiving callbacks for a network that they can't bring up anyways.
+     *
+     * @hide
+     */
+    // TODO : make @SystemApi when the impl is complete
+    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+    public void unofferNetwork(final @NonNull NetworkOfferCallback callback) {
+        final NetworkOfferCallbackProxy proxy = findProxyForCallback(callback);
+        if (null == proxy) return;
+        mProxies.remove(proxy);
+        mContext.getSystemService(ConnectivityManager.class).unofferNetwork(proxy);
+    }
 }
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
index 8c4f419..dd88c5a 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -311,7 +311,7 @@
          *
          * @see #addCapability(int)
          *
-         * @param capability The capability to add to unwanted capability list.
+         * @param capability The capability to add to forbidden capability list.
          * @return The builder to facilitate chaining.
          *
          * @hide
@@ -319,15 +319,15 @@
         @NonNull
         @SuppressLint("MissingGetterMatchingBuilder")
         @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-        public Builder addUnwantedCapability(@NetworkCapabilities.NetCapability int capability) {
-            mNetworkCapabilities.addUnwantedCapability(capability);
+        public Builder addForbiddenCapability(@NetworkCapabilities.NetCapability int capability) {
+            mNetworkCapabilities.addForbiddenCapability(capability);
             return this;
         }
 
         /**
-         * Removes (if found) the given unwanted capability from this builder instance.
+         * Removes (if found) the given forbidden capability from this builder instance.
          *
-         * @param capability The unwanted capability to remove.
+         * @param capability The forbidden capability to remove.
          * @return The builder to facilitate chaining.
          *
          * @hide
@@ -335,8 +335,9 @@
         @NonNull
         @SuppressLint("BuilderSetStyle")
         @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-        public Builder removeUnwantedCapability(@NetworkCapabilities.NetCapability int capability) {
-            mNetworkCapabilities.removeUnwantedCapability(capability);
+        public Builder removeForbiddenCapability(
+                @NetworkCapabilities.NetCapability int capability) {
+            mNetworkCapabilities.removeForbiddenCapability(capability);
             return this;
         }
 
@@ -598,13 +599,13 @@
     }
 
     /**
-     * @see Builder#addUnwantedCapability(int)
+     * @see Builder#addForbiddenCapability(int)
      *
      * @hide
      */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-    public boolean hasUnwantedCapability(@NetCapability int capability) {
-        return networkCapabilities.hasUnwantedCapability(capability);
+    public boolean hasForbiddenCapability(@NetCapability int capability) {
+        return networkCapabilities.hasForbiddenCapability(capability);
     }
 
     /**
@@ -709,18 +710,18 @@
     }
 
     /**
-     * Gets all the unwanted capabilities set on this {@code NetworkRequest} instance.
+     * Gets all the forbidden capabilities set on this {@code NetworkRequest} instance.
      *
-     * @return an array of unwanted capability values for this instance.
+     * @return an array of forbidden capability values for this instance.
      *
      * @hide
      */
     @NonNull
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-    public @NetCapability int[] getUnwantedCapabilities() {
-        // No need to make a defensive copy here as NC#getUnwantedCapabilities() already returns
+    public @NetCapability int[] getForbiddenCapabilities() {
+        // No need to make a defensive copy here as NC#getForbiddenCapabilities() already returns
         // a new array.
-        return networkCapabilities.getUnwantedCapabilities();
+        return networkCapabilities.getForbiddenCapabilities();
     }
 
     /**
diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp
index 1330e71..513de19 100644
--- a/packages/Connectivity/service/Android.bp
+++ b/packages/Connectivity/service/Android.bp
@@ -52,8 +52,8 @@
 java_library {
     name: "service-connectivity-pre-jarjar",
     srcs: [
+        "src/**/*.java",
         ":framework-connectivity-shared-srcs",
-        ":connectivity-service-srcs",
     ],
     libs: [
         "android.net.ipsec.ike",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/packages/Connectivity/service/src/com/android/server/ConnectivityService.java
similarity index 98%
rename from services/core/java/com/android/server/ConnectivityService.java
rename to packages/Connectivity/service/src/com/android/server/ConnectivityService.java
index b31adac..051a00b 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/packages/Connectivity/service/src/com/android/server/ConnectivityService.java
@@ -15,7 +15,6 @@
  */
 
 package com.android.server;
-
 import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
 import static android.content.pm.PackageManager.FEATURE_BLUETOOTH;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
@@ -34,7 +33,6 @@
 import static android.net.ConnectivityManager.BLOCKED_REASON_LOCKDOWN_VPN;
 import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
-import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
 import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
 import static android.net.ConnectivityManager.TYPE_ETHERNET;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
@@ -54,6 +52,7 @@
 import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
 import static android.net.ConnectivityManager.getNetworkTypeName;
 import static android.net.ConnectivityManager.isNetworkTypeValid;
+import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
@@ -125,6 +124,7 @@
 import android.net.INetworkAgent;
 import android.net.INetworkMonitor;
 import android.net.INetworkMonitorCallbacks;
+import android.net.INetworkOfferCallback;
 import android.net.IOnCompleteListener;
 import android.net.IQosCallback;
 import android.net.ISocketKeepaliveCallback;
@@ -133,6 +133,8 @@
 import android.net.IpPrefix;
 import android.net.LinkProperties;
 import android.net.MatchAllNetworkSpecifier;
+import android.net.NativeNetworkConfig;
+import android.net.NativeNetworkType;
 import android.net.NattSocketKeepalive;
 import android.net.Network;
 import android.net.NetworkAgent;
@@ -232,6 +234,7 @@
 import com.android.server.connectivity.AutodestructReference;
 import com.android.server.connectivity.DnsManager;
 import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
+import com.android.server.connectivity.FullScore;
 import com.android.server.connectivity.KeepaliveTracker;
 import com.android.server.connectivity.LingerMonitor;
 import com.android.server.connectivity.MockableSystemProperties;
@@ -239,6 +242,7 @@
 import com.android.server.connectivity.NetworkDiagnostics;
 import com.android.server.connectivity.NetworkNotificationManager;
 import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
+import com.android.server.connectivity.NetworkOffer;
 import com.android.server.connectivity.NetworkRanker;
 import com.android.server.connectivity.PermissionMonitor;
 import com.android.server.connectivity.ProfileNetworkPreferences;
@@ -602,6 +606,18 @@
     private static final int EVENT_UID_BLOCKED_REASON_CHANGED = 51;
 
     /**
+     * Event to register a new network offer
+     * obj = NetworkOffer
+     */
+    private static final int EVENT_REGISTER_NETWORK_OFFER = 52;
+
+    /**
+     * Event to unregister an existing network offer
+     * obj = INetworkOfferCallback
+     */
+    private static final int EVENT_UNREGISTER_NETWORK_OFFER = 53;
+
+    /**
      * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
      * should be shown.
      */
@@ -1378,7 +1394,7 @@
         // arguments like the handler or the DnsResolver.
         // TODO : remove this ; it is probably better handled with a sentinel request.
         mNoServiceNetwork = new NetworkAgentInfo(null,
-                new Network(NO_SERVICE_NET_ID),
+                new Network(INetd.UNREACHABLE_NET_ID),
                 new NetworkInfo(TYPE_NONE, 0, "", ""),
                 new LinkProperties(), new NetworkCapabilities(),
                 new NetworkScore.Builder().setLegacyInt(0).build(), mContext, null,
@@ -3804,36 +3820,43 @@
         nai.onNetworkDestroyed();
     }
 
-    private boolean createNativeNetwork(@NonNull NetworkAgentInfo networkAgent) {
+    private boolean createNativeNetwork(@NonNull NetworkAgentInfo nai) {
         try {
             // This should never fail.  Specifying an already in use NetID will cause failure.
-            if (networkAgent.isVPN()) {
-                mNetd.networkCreateVpn(networkAgent.network.getNetId(),
-                        (networkAgent.networkAgentConfig == null
-                                || !networkAgent.networkAgentConfig.allowBypass));
+            final NativeNetworkConfig config;
+            if (nai.isVPN()) {
+                if (getVpnType(nai) == VpnManager.TYPE_VPN_NONE) {
+                    Log.wtf(TAG, "Unable to get VPN type from network " + nai.toShortString());
+                    return false;
+                }
+                config = new NativeNetworkConfig(nai.network.getNetId(), NativeNetworkType.VIRTUAL,
+                        INetd.PERMISSION_NONE,
+                        (nai.networkAgentConfig == null || !nai.networkAgentConfig.allowBypass),
+                        getVpnType(nai));
             } else {
-                mNetd.networkCreatePhysical(networkAgent.network.getNetId(),
-                        getNetworkPermission(networkAgent.networkCapabilities));
+                config = new NativeNetworkConfig(nai.network.getNetId(), NativeNetworkType.PHYSICAL,
+                        getNetworkPermission(nai.networkCapabilities), /*secure=*/ false,
+                        VpnManager.TYPE_VPN_NONE);
             }
-            mDnsResolver.createNetworkCache(networkAgent.network.getNetId());
-            mDnsManager.updateTransportsForNetwork(networkAgent.network.getNetId(),
-                    networkAgent.networkCapabilities.getTransportTypes());
+            mNetd.networkCreate(config);
+            mDnsResolver.createNetworkCache(nai.network.getNetId());
+            mDnsManager.updateTransportsForNetwork(nai.network.getNetId(),
+                    nai.networkCapabilities.getTransportTypes());
             return true;
         } catch (RemoteException | ServiceSpecificException e) {
-            loge("Error creating network " + networkAgent.network.getNetId() + ": "
-                    + e.getMessage());
+            loge("Error creating network " + nai.toShortString() + ": " + e.getMessage());
             return false;
         }
     }
 
-    private void destroyNativeNetwork(@NonNull NetworkAgentInfo networkAgent) {
+    private void destroyNativeNetwork(@NonNull NetworkAgentInfo nai) {
         try {
-            mNetd.networkDestroy(networkAgent.network.getNetId());
+            mNetd.networkDestroy(nai.network.getNetId());
         } catch (RemoteException | ServiceSpecificException e) {
             loge("Exception destroying network(networkDestroy): " + e);
         }
         try {
-            mDnsResolver.destroyNetworkCache(networkAgent.network.getNetId());
+            mDnsResolver.destroyNetworkCache(nai.network.getNetId());
         } catch (RemoteException | ServiceSpecificException e) {
             loge("Exception destroying network: " + e);
         }
@@ -4675,6 +4698,18 @@
                     handleUnregisterNetworkProvider((Messenger) msg.obj);
                     break;
                 }
+                case EVENT_REGISTER_NETWORK_OFFER: {
+                    handleRegisterNetworkOffer((NetworkOffer) msg.obj);
+                    break;
+                }
+                case EVENT_UNREGISTER_NETWORK_OFFER: {
+                    final NetworkOfferInfo offer =
+                            findNetworkOfferInfoByCallback((INetworkOfferCallback) msg.obj);
+                    if (null != offer) {
+                        handleUnregisterNetworkOffer(offer);
+                    }
+                    break;
+                }
                 case EVENT_REGISTER_NETWORK_AGENT: {
                     final Pair<NetworkAgentInfo, INetworkMonitor> arg =
                             (Pair<NetworkAgentInfo, INetworkMonitor>) msg.obj;
@@ -6205,12 +6240,37 @@
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_PROVIDER, messenger));
     }
 
+    @Override
+    public void offerNetwork(@NonNull final Messenger providerMessenger,
+            @NonNull final NetworkScore score, @NonNull final NetworkCapabilities caps,
+            @NonNull final INetworkOfferCallback callback) {
+        final NetworkOffer offer = new NetworkOffer(
+                FullScore.makeProspectiveScore(score, caps), caps, callback, providerMessenger);
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_OFFER, offer));
+    }
+
+    @Override
+    public void unofferNetwork(@NonNull final INetworkOfferCallback callback) {
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_OFFER, callback));
+    }
+
     private void handleUnregisterNetworkProvider(Messenger messenger) {
         NetworkProviderInfo npi = mNetworkProviderInfos.remove(messenger);
         if (npi == null) {
             loge("Failed to find Messenger in unregisterNetworkProvider");
             return;
         }
+        // Unregister all the offers from this provider
+        final ArrayList<NetworkOfferInfo> toRemove = new ArrayList<>();
+        for (final NetworkOfferInfo noi : mNetworkOffers) {
+            if (noi.offer.provider == messenger) {
+                // Can't call handleUnregisterNetworkOffer here because iteration is in progress
+                toRemove.add(noi);
+            }
+        }
+        for (NetworkOfferInfo noi : toRemove) {
+            handleUnregisterNetworkOffer(noi);
+        }
         if (DBG) log("unregisterNetworkProvider for " + npi.name);
     }
 
@@ -6249,6 +6309,10 @@
     // (on the handler thread).
     private volatile List<UidRange> mVpnBlockedUidRanges = new ArrayList<>();
 
+    // Must only be accessed on the handler thread
+    @NonNull
+    private final ArrayList<NetworkOfferInfo> mNetworkOffers = new ArrayList<>();
+
     @GuardedBy("mBlockedAppUids")
     private final HashSet<Integer> mBlockedAppUids = new HashSet<>();
 
@@ -6424,8 +6488,6 @@
     // Request used to optionally keep vehicle internal network always active
     private final NetworkRequest mDefaultVehicleRequest;
 
-    // TODO replace with INetd.UNREACHABLE_NET_ID when available.
-    private static final int NO_SERVICE_NET_ID = 52;
     // Sentinel NAI used to direct apps with default networks that should have no connectivity to a
     // network with no service. This NAI should never be matched against, nor should any public API
     // ever return the associated network. For this reason, this NAI is not in the list of available
@@ -6573,6 +6635,65 @@
         updateUids(nai, null, nai.networkCapabilities);
     }
 
+    private class NetworkOfferInfo implements IBinder.DeathRecipient {
+        @NonNull public final NetworkOffer offer;
+
+        NetworkOfferInfo(@NonNull final NetworkOffer offer) {
+            this.offer = offer;
+        }
+
+        @Override
+        public void binderDied() {
+            mHandler.post(() -> handleUnregisterNetworkOffer(this));
+        }
+    }
+
+    /**
+     * Register or update a network offer.
+     * @param newOffer The new offer. If the callback member is the same as an existing
+     *                 offer, it is an update of that offer.
+     */
+    private void handleRegisterNetworkOffer(@NonNull final NetworkOffer newOffer) {
+        ensureRunningOnConnectivityServiceThread();
+        if (null == mNetworkProviderInfos.get(newOffer.provider)) {
+            // This may actually happen if a provider updates its score or registers and then
+            // immediately unregisters. The offer would still be in the handler queue, but the
+            // provider would have been removed.
+            if (DBG) log("Received offer from an unregistered provider");
+            return;
+        }
+
+        final NetworkOfferInfo existingOffer = findNetworkOfferInfoByCallback(newOffer.callback);
+        if (null != existingOffer) {
+            handleUnregisterNetworkOffer(existingOffer);
+            newOffer.migrateFrom(existingOffer.offer);
+        }
+        final NetworkOfferInfo noi = new NetworkOfferInfo(newOffer);
+        try {
+            noi.offer.provider.getBinder().linkToDeath(noi, 0 /* flags */);
+        } catch (RemoteException e) {
+            noi.binderDied();
+            return;
+        }
+        mNetworkOffers.add(noi);
+        // TODO : send requests to the provider.
+    }
+
+    private void handleUnregisterNetworkOffer(@NonNull final NetworkOfferInfo noi) {
+        ensureRunningOnConnectivityServiceThread();
+        mNetworkOffers.remove(noi);
+        noi.offer.provider.getBinder().unlinkToDeath(noi, 0 /* flags */);
+    }
+
+    @Nullable private NetworkOfferInfo findNetworkOfferInfoByCallback(
+            @NonNull final INetworkOfferCallback callback) {
+        ensureRunningOnConnectivityServiceThread();
+        for (final NetworkOfferInfo noi : mNetworkOffers) {
+            if (noi.offer.callback.equals(callback)) return noi;
+        }
+        return null;
+    }
+
     /**
      * Called when receiving LinkProperties directly from a NetworkAgent.
      * Stores into |nai| any data coming from the agent that might also be written to the network's
@@ -8572,8 +8693,7 @@
 
         // restore private DNS settings to default mode (opportunistic)
         if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_PRIVATE_DNS)) {
-            Settings.Global.putString(mContext.getContentResolver(),
-                    ConnectivitySettingsManager.PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_OPPORTUNISTIC);
+            ConnectivitySettingsManager.setPrivateDnsMode(mContext, PRIVATE_DNS_MODE_OPPORTUNISTIC);
         }
 
         Settings.Global.putString(mContext.getContentResolver(),
@@ -9033,7 +9153,8 @@
     }
 
     private NetworkCapabilities getNetworkCapabilitiesWithoutUids(@NonNull NetworkCapabilities nc) {
-        final NetworkCapabilities sanitized = new NetworkCapabilities(nc);
+        final NetworkCapabilities sanitized = new NetworkCapabilities(nc,
+                NetworkCapabilities.REDACT_ALL);
         sanitized.setUids(null);
         sanitized.setAdministratorUids(new int[0]);
         sanitized.setOwnerUid(Process.INVALID_UID);
diff --git a/services/core/java/com/android/server/ConnectivityServiceInitializer.java b/packages/Connectivity/service/src/com/android/server/ConnectivityServiceInitializer.java
similarity index 100%
rename from services/core/java/com/android/server/ConnectivityServiceInitializer.java
rename to packages/Connectivity/service/src/com/android/server/ConnectivityServiceInitializer.java
diff --git a/services/core/java/com/android/server/NetIdManager.java b/packages/Connectivity/service/src/com/android/server/NetIdManager.java
similarity index 100%
rename from services/core/java/com/android/server/NetIdManager.java
rename to packages/Connectivity/service/src/com/android/server/NetIdManager.java
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/packages/Connectivity/service/src/com/android/server/TestNetworkService.java
similarity index 100%
rename from services/core/java/com/android/server/TestNetworkService.java
rename to packages/Connectivity/service/src/com/android/server/TestNetworkService.java
diff --git a/services/core/java/com/android/server/connectivity/AutodestructReference.java b/packages/Connectivity/service/src/com/android/server/connectivity/AutodestructReference.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/AutodestructReference.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/AutodestructReference.java
diff --git a/services/core/java/com/android/server/connectivity/ConnectivityConstants.java b/packages/Connectivity/service/src/com/android/server/connectivity/ConnectivityConstants.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/ConnectivityConstants.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/ConnectivityConstants.java
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/packages/Connectivity/service/src/com/android/server/connectivity/DnsManager.java
similarity index 97%
rename from services/core/java/com/android/server/connectivity/DnsManager.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/DnsManager.java
index cf4fe1e..05b12ba 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/packages/Connectivity/service/src/com/android/server/connectivity/DnsManager.java
@@ -16,14 +16,14 @@
 
 package com.android.server.connectivity;
 
-import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
-import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
 import static android.net.ConnectivitySettingsManager.DNS_RESOLVER_MAX_SAMPLES;
 import static android.net.ConnectivitySettingsManager.DNS_RESOLVER_MIN_SAMPLES;
 import static android.net.ConnectivitySettingsManager.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS;
 import static android.net.ConnectivitySettingsManager.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT;
 import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_DEFAULT_MODE;
 import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE;
+import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF;
+import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
 import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_SPECIFIER;
 import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_FAILURE;
 import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_SUCCESS;
@@ -33,6 +33,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.ConnectivityManager;
+import android.net.ConnectivitySettingsManager;
 import android.net.IDnsResolver;
 import android.net.InetAddresses;
 import android.net.LinkProperties;
@@ -131,11 +132,11 @@
      * Get PrivateDnsConfig.
      */
     public static PrivateDnsConfig getPrivateDnsConfig(Context context) {
-        final String mode = ConnectivityManager.getPrivateDnsMode(context);
+        final int mode = ConnectivitySettingsManager.getPrivateDnsMode(context);
 
-        final boolean useTls = !TextUtils.isEmpty(mode) && !PRIVATE_DNS_MODE_OFF.equals(mode);
+        final boolean useTls = mode != PRIVATE_DNS_MODE_OFF;
 
-        if (PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals(mode)) {
+        if (PRIVATE_DNS_MODE_PROVIDER_HOSTNAME == mode) {
             final String specifier = getStringSetting(context.getContentResolver(),
                     PRIVATE_DNS_SPECIFIER);
             return new PrivateDnsConfig(specifier, null);
diff --git a/services/core/java/com/android/server/connectivity/FullScore.java b/packages/Connectivity/service/src/com/android/server/connectivity/FullScore.java
similarity index 83%
rename from services/core/java/com/android/server/connectivity/FullScore.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/FullScore.java
index 028cfee..9326d69 100644
--- a/services/core/java/com/android/server/connectivity/FullScore.java
+++ b/packages/Connectivity/service/src/com/android/server/connectivity/FullScore.java
@@ -16,6 +16,7 @@
 
 package com.android.server.connectivity;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 
@@ -116,6 +117,33 @@
     }
 
     /**
+     * Given a score supplied by the NetworkAgent, produce a prospective score for an offer.
+     *
+     * NetworkOffers have score filters that are compared to the scores of actual networks
+     * to see if they could possibly beat the current satisfier. Some things the agent can't
+     * know in advance ; a good example is the validation bit – some networks will validate,
+     * others won't. For comparison purposes, assume the best, so all possibly beneficial
+     * networks will be brought up.
+     *
+     * @param score the score supplied by the agent for this offer
+     * @param caps the capabilities supplied by the agent for this offer
+     * @return a FullScore appropriate for comparing to actual network's scores.
+     */
+    public static FullScore makeProspectiveScore(@NonNull final NetworkScore score,
+            @NonNull final NetworkCapabilities caps) {
+        // If the network offers Internet access, it may validate.
+        final boolean mayValidate = caps.hasCapability(NET_CAPABILITY_INTERNET);
+        // VPN transports are known in advance.
+        final boolean vpn = caps.hasTransport(TRANSPORT_VPN);
+        // The network hasn't been chosen by the user (yet, at least).
+        final boolean everUserSelected = false;
+        // Don't assume the user will accept unvalidated connectivity.
+        final boolean acceptUnvalidated = false;
+        return withPolicies(score.getLegacyInt(), mayValidate, vpn, everUserSelected,
+                acceptUnvalidated);
+    }
+
+    /**
      * Return a new score given updated caps and config.
      *
      * @param caps the NetworkCapabilities of the network
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/packages/Connectivity/service/src/com/android/server/connectivity/KeepaliveTracker.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/KeepaliveTracker.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/KeepaliveTracker.java
diff --git a/services/core/java/com/android/server/connectivity/LingerMonitor.java b/packages/Connectivity/service/src/com/android/server/connectivity/LingerMonitor.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/LingerMonitor.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/LingerMonitor.java
diff --git a/services/core/java/com/android/server/connectivity/MockableSystemProperties.java b/packages/Connectivity/service/src/com/android/server/connectivity/MockableSystemProperties.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/MockableSystemProperties.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/MockableSystemProperties.java
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/packages/Connectivity/service/src/com/android/server/connectivity/Nat464Xlat.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/Nat464Xlat.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/Nat464Xlat.java
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkAgentInfo.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/NetworkAgentInfo.java
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkDiagnostics.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/NetworkDiagnostics.java
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkNotificationManager.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/NetworkNotificationManager.java
diff --git a/packages/Connectivity/service/src/com/android/server/connectivity/NetworkOffer.java b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkOffer.java
new file mode 100644
index 0000000..fa2d465
--- /dev/null
+++ b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkOffer.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2021 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 com.android.server.connectivity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.INetworkOfferCallback;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.os.Messenger;
+
+import java.util.Objects;
+
+
+/**
+ * Represents an offer made by a NetworkProvider to create a network if a need arises.
+ *
+ * This class contains the prospective score and capabilities of the network. The provider
+ * is not obligated to caps able to create a network satisfying this, nor to build a network
+ * with the exact score and/or capabilities passed ; after all, not all providers know in
+ * advance what a network will look like after it's connected. Instead, this is meant as a
+ * filter to limit requests sent to the provider by connectivity to those that this offer stands
+ * a chance to fulfill.
+ *
+ * @see NetworkProvider#offerNetwork.
+ *
+ * @hide
+ */
+public class NetworkOffer {
+    @NonNull public final FullScore score;
+    @NonNull public final NetworkCapabilities caps;
+    @NonNull public final INetworkOfferCallback callback;
+    @NonNull public final Messenger provider;
+
+    private static NetworkCapabilities emptyCaps() {
+        final NetworkCapabilities nc = new NetworkCapabilities();
+        return nc;
+    }
+
+    // Ideally the caps argument would be non-null, but null has historically meant no filter
+    // and telephony passes null. Keep backward compatibility.
+    public NetworkOffer(@NonNull final FullScore score,
+            @Nullable final NetworkCapabilities caps,
+            @NonNull final INetworkOfferCallback callback,
+            @NonNull final Messenger provider) {
+        this.score = Objects.requireNonNull(score);
+        this.caps = null != caps ? caps : emptyCaps();
+        this.callback = Objects.requireNonNull(callback);
+        this.provider = Objects.requireNonNull(provider);
+    }
+
+    /**
+     * Migrate from, and take over, a previous offer.
+     *
+     * When an updated offer is sent from a provider, call this method on the new offer, passing
+     * the old one, to take over the state.
+     *
+     * @param previousOffer
+     */
+    public void migrateFrom(@NonNull final NetworkOffer previousOffer) {
+        if (!callback.equals(previousOffer.callback)) {
+            throw new IllegalArgumentException("Can only migrate from a previous version of"
+                    + " the same offer");
+        }
+    }
+
+    /**
+     * Returns whether an offer can satisfy a NetworkRequest, according to its capabilities.
+     * @param request The request to test against.
+     * @return Whether this offer can satisfy the request.
+     */
+    public final boolean canSatisfy(@NonNull final NetworkRequest request) {
+        return request.networkCapabilities.satisfiedByNetworkCapabilities(caps);
+    }
+
+    @Override
+    public String toString() {
+        return "NetworkOffer [ Score " + score + " ]";
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/NetworkRanker.java b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkRanker.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/NetworkRanker.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/NetworkRanker.java
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/packages/Connectivity/service/src/com/android/server/connectivity/PermissionMonitor.java
similarity index 99%
rename from services/core/java/com/android/server/connectivity/PermissionMonitor.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/PermissionMonitor.java
index 7837e6e..506cadb 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/packages/Connectivity/service/src/com/android/server/connectivity/PermissionMonitor.java
@@ -338,7 +338,8 @@
             return currentPermission;
         }
         try {
-            final PackageInfo app = mPackageManager.getPackageInfo(name, GET_PERMISSIONS);
+            final PackageInfo app = mPackageManager.getPackageInfo(name,
+                    GET_PERMISSIONS | MATCH_ANY_USER);
             final boolean isNetwork = hasNetworkPermission(app);
             final boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
             if (isNetwork || hasRestrictedPermission) {
@@ -664,6 +665,7 @@
                     break;
                 case INetd.PERMISSION_UNINSTALLED:
                     uninstalledAppIds.add(netdPermissionsAppIds.keyAt(i));
+                    break;
                 default:
                     Log.e(TAG, "unknown permission type: " + permissions + "for uid: "
                             + netdPermissionsAppIds.keyAt(i));
diff --git a/services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java b/packages/Connectivity/service/src/com/android/server/connectivity/ProfileNetworkPreferences.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/ProfileNetworkPreferences.java
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/packages/Connectivity/service/src/com/android/server/connectivity/ProxyTracker.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/ProxyTracker.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/ProxyTracker.java
diff --git a/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java b/packages/Connectivity/service/src/com/android/server/connectivity/QosCallbackAgentConnection.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/QosCallbackAgentConnection.java
diff --git a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java b/packages/Connectivity/service/src/com/android/server/connectivity/QosCallbackTracker.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/QosCallbackTracker.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/QosCallbackTracker.java
diff --git a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java b/packages/Connectivity/service/src/com/android/server/connectivity/TcpKeepaliveController.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
rename to packages/Connectivity/service/src/com/android/server/connectivity/TcpKeepaliveController.java
diff --git a/tests/net/OWNERS b/packages/Connectivity/tests/OWNERS
similarity index 100%
rename from tests/net/OWNERS
rename to packages/Connectivity/tests/OWNERS
diff --git a/tests/net/TEST_MAPPING b/packages/Connectivity/tests/TEST_MAPPING
similarity index 100%
rename from tests/net/TEST_MAPPING
rename to packages/Connectivity/tests/TEST_MAPPING
diff --git a/tests/net/common/Android.bp b/packages/Connectivity/tests/common/Android.bp
similarity index 100%
rename from tests/net/common/Android.bp
rename to packages/Connectivity/tests/common/Android.bp
diff --git a/tests/net/common/java/ParseExceptionTest.kt b/packages/Connectivity/tests/common/java/ParseExceptionTest.kt
similarity index 100%
rename from tests/net/common/java/ParseExceptionTest.kt
rename to packages/Connectivity/tests/common/java/ParseExceptionTest.kt
diff --git a/tests/net/common/java/android/net/CaptivePortalDataTest.kt b/packages/Connectivity/tests/common/java/android/net/CaptivePortalDataTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/CaptivePortalDataTest.kt
rename to packages/Connectivity/tests/common/java/android/net/CaptivePortalDataTest.kt
diff --git a/tests/net/common/java/android/net/CaptivePortalTest.java b/packages/Connectivity/tests/common/java/android/net/CaptivePortalTest.java
similarity index 100%
rename from tests/net/common/java/android/net/CaptivePortalTest.java
rename to packages/Connectivity/tests/common/java/android/net/CaptivePortalTest.java
diff --git a/tests/net/common/java/android/net/DependenciesTest.java b/packages/Connectivity/tests/common/java/android/net/DependenciesTest.java
similarity index 100%
rename from tests/net/common/java/android/net/DependenciesTest.java
rename to packages/Connectivity/tests/common/java/android/net/DependenciesTest.java
diff --git a/tests/net/common/java/android/net/DhcpInfoTest.java b/packages/Connectivity/tests/common/java/android/net/DhcpInfoTest.java
similarity index 100%
rename from tests/net/common/java/android/net/DhcpInfoTest.java
rename to packages/Connectivity/tests/common/java/android/net/DhcpInfoTest.java
diff --git a/tests/net/common/java/android/net/IpPrefixTest.java b/packages/Connectivity/tests/common/java/android/net/IpPrefixTest.java
similarity index 100%
rename from tests/net/common/java/android/net/IpPrefixTest.java
rename to packages/Connectivity/tests/common/java/android/net/IpPrefixTest.java
diff --git a/tests/net/common/java/android/net/KeepalivePacketDataTest.kt b/packages/Connectivity/tests/common/java/android/net/KeepalivePacketDataTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/KeepalivePacketDataTest.kt
rename to packages/Connectivity/tests/common/java/android/net/KeepalivePacketDataTest.kt
diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/packages/Connectivity/tests/common/java/android/net/LinkAddressTest.java
similarity index 100%
rename from tests/net/common/java/android/net/LinkAddressTest.java
rename to packages/Connectivity/tests/common/java/android/net/LinkAddressTest.java
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/packages/Connectivity/tests/common/java/android/net/LinkPropertiesTest.java
similarity index 100%
rename from tests/net/common/java/android/net/LinkPropertiesTest.java
rename to packages/Connectivity/tests/common/java/android/net/LinkPropertiesTest.java
diff --git a/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt b/packages/Connectivity/tests/common/java/android/net/MatchAllNetworkSpecifierTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt
rename to packages/Connectivity/tests/common/java/android/net/MatchAllNetworkSpecifierTest.kt
diff --git a/tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt b/packages/Connectivity/tests/common/java/android/net/NattKeepalivePacketDataTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt
rename to packages/Connectivity/tests/common/java/android/net/NattKeepalivePacketDataTest.kt
diff --git a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt b/packages/Connectivity/tests/common/java/android/net/NetworkAgentConfigTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/NetworkAgentConfigTest.kt
rename to packages/Connectivity/tests/common/java/android/net/NetworkAgentConfigTest.kt
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/packages/Connectivity/tests/common/java/android/net/NetworkCapabilitiesTest.java
similarity index 92%
rename from tests/net/common/java/android/net/NetworkCapabilitiesTest.java
rename to packages/Connectivity/tests/common/java/android/net/NetworkCapabilitiesTest.java
index 33f2c67..d74b802 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/packages/Connectivity/tests/common/java/android/net/NetworkCapabilitiesTest.java
@@ -340,7 +340,7 @@
 
     private void testParcelSane(NetworkCapabilities cap) {
         if (isAtLeastS()) {
-            assertParcelSane(cap, 17);
+            assertParcelSane(cap, 16);
         } else if (isAtLeastR()) {
             assertParcelSane(cap, 15);
         } else {
@@ -390,9 +390,11 @@
     @Test
     public void testOemPaid() {
         NetworkCapabilities nc = new NetworkCapabilities();
-        // By default OEM_PAID is neither in the unwanted or required lists and the network is not
+        // By default OEM_PAID is neither in the required or forbidden lists and the network is not
         // restricted.
-        assertFalse(nc.hasUnwantedCapability(NET_CAPABILITY_OEM_PAID));
+        if (isAtLeastS()) {
+            assertFalse(nc.hasForbiddenCapability(NET_CAPABILITY_OEM_PAID));
+        }
         assertFalse(nc.hasCapability(NET_CAPABILITY_OEM_PAID));
         nc.maybeMarkCapabilitiesRestricted();
         assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
@@ -417,9 +419,9 @@
     @Test @IgnoreUpTo(Build.VERSION_CODES.R)
     public void testOemPrivate() {
         NetworkCapabilities nc = new NetworkCapabilities();
-        // By default OEM_PRIVATE is neither in the unwanted or required lists and the network is
+        // By default OEM_PRIVATE is neither in the required or forbidden lists and the network is
         // not restricted.
-        assertFalse(nc.hasUnwantedCapability(NET_CAPABILITY_OEM_PRIVATE));
+        assertFalse(nc.hasForbiddenCapability(NET_CAPABILITY_OEM_PRIVATE));
         assertFalse(nc.hasCapability(NET_CAPABILITY_OEM_PRIVATE));
         nc.maybeMarkCapabilitiesRestricted();
         assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
@@ -441,8 +443,8 @@
         assertFalse(nr.satisfiedByNetworkCapabilities(new NetworkCapabilities()));
     }
 
-    @Test
-    public void testUnwantedCapabilities() {
+    @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+    public void testForbiddenCapabilities() {
         NetworkCapabilities network = new NetworkCapabilities();
 
         NetworkCapabilities request = new NetworkCapabilities();
@@ -450,19 +452,19 @@
                 request.satisfiedByNetworkCapabilities(network));
 
         // Requesting absence of capabilities that network doesn't have. Request should satisfy.
-        request.addUnwantedCapability(NET_CAPABILITY_WIFI_P2P);
-        request.addUnwantedCapability(NET_CAPABILITY_NOT_METERED);
+        request.addForbiddenCapability(NET_CAPABILITY_WIFI_P2P);
+        request.addForbiddenCapability(NET_CAPABILITY_NOT_METERED);
         assertTrue(request.satisfiedByNetworkCapabilities(network));
-        assertArrayEquals(new int[] {NET_CAPABILITY_WIFI_P2P,
+        assertArrayEquals(new int[]{NET_CAPABILITY_WIFI_P2P,
                         NET_CAPABILITY_NOT_METERED},
-                request.getUnwantedCapabilities());
+                request.getForbiddenCapabilities());
 
         // This is a default capability, just want to make sure its there because we use it below.
         assertTrue(network.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
 
-        // Verify that adding unwanted capability will effectively remove it from capability list.
-        request.addUnwantedCapability(NET_CAPABILITY_NOT_RESTRICTED);
-        assertTrue(request.hasUnwantedCapability(NET_CAPABILITY_NOT_RESTRICTED));
+        // Verify that adding forbidden capability will effectively remove it from capability list.
+        request.addForbiddenCapability(NET_CAPABILITY_NOT_RESTRICTED);
+        assertTrue(request.hasForbiddenCapability(NET_CAPABILITY_NOT_RESTRICTED));
         assertFalse(request.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
 
         // Now this request won't be satisfied because network contains NOT_RESTRICTED.
@@ -470,10 +472,10 @@
         network.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
         assertTrue(request.satisfiedByNetworkCapabilities(network));
 
-        // Verify that adding capability will effectively remove it from unwanted list
+        // Verify that adding capability will effectively remove it from forbidden list
         request.addCapability(NET_CAPABILITY_NOT_RESTRICTED);
         assertTrue(request.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
-        assertFalse(request.hasUnwantedCapability(NET_CAPABILITY_NOT_RESTRICTED));
+        assertFalse(request.hasForbiddenCapability(NET_CAPABILITY_NOT_RESTRICTED));
 
         assertFalse(request.satisfiedByNetworkCapabilities(network));
         network.addCapability(NET_CAPABILITY_NOT_RESTRICTED);
@@ -512,24 +514,20 @@
         assertTrue(nc1.equalsNetCapabilities(nc2));
         assertEquals(nc1, nc2);
 
-        nc1.addUnwantedCapability(NET_CAPABILITY_INTERNET);
-        assertFalse(nc1.equalsNetCapabilities(nc2));
-        nc2.addUnwantedCapability(NET_CAPABILITY_INTERNET);
-        assertTrue(nc1.equalsNetCapabilities(nc2));
         if (isAtLeastS()) {
-            // Remove a required capability doesn't affect unwanted capabilities.
-            // This is a behaviour change from S.
+            nc1.addForbiddenCapability(NET_CAPABILITY_INTERNET);
+            assertFalse(nc1.equalsNetCapabilities(nc2));
+            nc2.addForbiddenCapability(NET_CAPABILITY_INTERNET);
+            assertTrue(nc1.equalsNetCapabilities(nc2));
+
+            // Remove a required capability doesn't affect forbidden capabilities.
+            // This is a behaviour change from R to S.
             nc1.removeCapability(NET_CAPABILITY_INTERNET);
             assertTrue(nc1.equalsNetCapabilities(nc2));
 
-            nc1.removeUnwantedCapability(NET_CAPABILITY_INTERNET);
+            nc1.removeForbiddenCapability(NET_CAPABILITY_INTERNET);
             assertFalse(nc1.equalsNetCapabilities(nc2));
-            nc2.removeUnwantedCapability(NET_CAPABILITY_INTERNET);
-            assertTrue(nc1.equalsNetCapabilities(nc2));
-        } else {
-            nc1.removeCapability(NET_CAPABILITY_INTERNET);
-            assertFalse(nc1.equalsNetCapabilities(nc2));
-            nc2.removeCapability(NET_CAPABILITY_INTERNET);
+            nc2.removeForbiddenCapability(NET_CAPABILITY_INTERNET);
             assertTrue(nc1.equalsNetCapabilities(nc2));
         }
     }
@@ -581,31 +579,25 @@
         NetworkCapabilities nc1 = new NetworkCapabilities();
         NetworkCapabilities nc2 = new NetworkCapabilities();
 
-        nc1.addUnwantedCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
+        if (isAtLeastS()) {
+            nc1.addForbiddenCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
+        }
         nc1.addCapability(NET_CAPABILITY_NOT_ROAMING);
         assertNotEquals(nc1, nc2);
         nc2.combineCapabilities(nc1);
         assertEquals(nc1, nc2);
         assertTrue(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
-        assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_CAPTIVE_PORTAL));
-
-        // This will effectively move NOT_ROAMING capability from required to unwanted for nc1.
-        nc1.addUnwantedCapability(NET_CAPABILITY_NOT_ROAMING);
+        if (isAtLeastS()) {
+            assertTrue(nc2.hasForbiddenCapability(NET_CAPABILITY_CAPTIVE_PORTAL));
+        }
 
         if (isAtLeastS()) {
-            // From S, it is not allowed to have the same capability in both wanted and
-            // unwanted list.
+            // This will effectively move NOT_ROAMING capability from required to forbidden for nc1.
+            nc1.addForbiddenCapability(NET_CAPABILITY_NOT_ROAMING);
+            // It is not allowed to have the same capability in both wanted and forbidden list.
             assertThrows(IllegalArgumentException.class, () -> nc2.combineCapabilities(nc1));
-            // Remove unwanted capability to continue other tests.
-            nc1.removeUnwantedCapability(NET_CAPABILITY_NOT_ROAMING);
-        } else {
-            nc2.combineCapabilities(nc1);
-            // We will get this capability in both requested and unwanted lists thus this request
-            // will never be satisfied.
-            assertTrue(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
-            assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_NOT_ROAMING));
-            // For R or below, remove unwanted capability via removeCapability.
-            nc1.removeCapability(NET_CAPABILITY_NOT_ROAMING);
+            // Remove forbidden capability to continue other tests.
+            nc1.removeForbiddenCapability(NET_CAPABILITY_NOT_ROAMING);
         }
 
         nc1.setSSID(TEST_SSID);
@@ -683,14 +675,11 @@
     public void testSetCapabilities() {
         final int[] REQUIRED_CAPABILITIES = new int[] {
                 NET_CAPABILITY_INTERNET, NET_CAPABILITY_NOT_VPN };
-        final int[] UNWANTED_CAPABILITIES = new int[] {
-                NET_CAPABILITY_NOT_RESTRICTED, NET_CAPABILITY_NOT_METERED
-        };
 
         NetworkCapabilities nc1 = new NetworkCapabilities();
         NetworkCapabilities nc2 = new NetworkCapabilities();
 
-        nc1.setCapabilities(REQUIRED_CAPABILITIES, UNWANTED_CAPABILITIES);
+        nc1.setCapabilities(REQUIRED_CAPABILITIES);
         assertArrayEquals(REQUIRED_CAPABILITIES, nc1.getCapabilities());
 
         // Verify that setting and adding capabilities leads to the same object state.
@@ -698,10 +687,25 @@
         for (int cap : REQUIRED_CAPABILITIES) {
             nc2.addCapability(cap);
         }
-        for (int cap : UNWANTED_CAPABILITIES) {
-            nc2.addUnwantedCapability(cap);
-        }
         assertEquals(nc1, nc2);
+
+        if (isAtLeastS()) {
+            final int[] forbiddenCapabilities = new int[]{
+                    NET_CAPABILITY_NOT_METERED, NET_CAPABILITY_NOT_RESTRICTED };
+
+            nc1.setCapabilities(REQUIRED_CAPABILITIES, forbiddenCapabilities);
+            assertArrayEquals(REQUIRED_CAPABILITIES, nc1.getCapabilities());
+            assertArrayEquals(forbiddenCapabilities, nc1.getForbiddenCapabilities());
+
+            nc2.clearAll();
+            for (int cap : REQUIRED_CAPABILITIES) {
+                nc2.addCapability(cap);
+            }
+            for (int cap : forbiddenCapabilities) {
+                nc2.addForbiddenCapability(cap);
+            }
+            assertEquals(nc1, nc2);
+        }
     }
 
     @Test
@@ -769,23 +773,32 @@
         NetworkCapabilities nc1 = new NetworkCapabilities();
         NetworkCapabilities nc2 = new NetworkCapabilities();
 
-        nc1.addUnwantedCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
+        if (isAtLeastS()) {
+            nc1.addForbiddenCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
+        }
         nc1.addCapability(NET_CAPABILITY_NOT_ROAMING);
         assertNotEquals(nc1, nc2);
         nc2.set(nc1);
         assertEquals(nc1, nc2);
         assertTrue(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
-        assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_CAPTIVE_PORTAL));
+        if (isAtLeastS()) {
+            assertTrue(nc2.hasForbiddenCapability(NET_CAPABILITY_CAPTIVE_PORTAL));
+        }
 
-        // This will effectively move NOT_ROAMING capability from required to unwanted for nc1.
-        nc1.addUnwantedCapability(NET_CAPABILITY_NOT_ROAMING);
+        if (isAtLeastS()) {
+            // This will effectively move NOT_ROAMING capability from required to forbidden for nc1.
+            nc1.addForbiddenCapability(NET_CAPABILITY_NOT_ROAMING);
+        }
         nc1.setSSID(TEST_SSID);
         nc2.set(nc1);
         assertEquals(nc1, nc2);
-        // Contrary to combineCapabilities, set() will have removed the NOT_ROAMING capability
-        // from nc2.
-        assertFalse(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
-        assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_NOT_ROAMING));
+        if (isAtLeastS()) {
+            // Contrary to combineCapabilities, set() will have removed the NOT_ROAMING capability
+            // from nc2.
+            assertFalse(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+            assertTrue(nc2.hasForbiddenCapability(NET_CAPABILITY_NOT_ROAMING));
+        }
+
         if (isAtLeastR()) {
             assertTrue(TEST_SSID.equals(nc2.getSsid()));
         }
diff --git a/tests/net/common/java/android/net/NetworkProviderTest.kt b/packages/Connectivity/tests/common/java/android/net/NetworkProviderTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/NetworkProviderTest.kt
rename to packages/Connectivity/tests/common/java/android/net/NetworkProviderTest.kt
diff --git a/tests/net/common/java/android/net/NetworkSpecifierTest.kt b/packages/Connectivity/tests/common/java/android/net/NetworkSpecifierTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/NetworkSpecifierTest.kt
rename to packages/Connectivity/tests/common/java/android/net/NetworkSpecifierTest.kt
diff --git a/tests/net/common/java/android/net/NetworkStackTest.java b/packages/Connectivity/tests/common/java/android/net/NetworkStackTest.java
similarity index 100%
rename from tests/net/common/java/android/net/NetworkStackTest.java
rename to packages/Connectivity/tests/common/java/android/net/NetworkStackTest.java
diff --git a/tests/net/common/java/android/net/NetworkStateSnapshotTest.kt b/packages/Connectivity/tests/common/java/android/net/NetworkStateSnapshotTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/NetworkStateSnapshotTest.kt
rename to packages/Connectivity/tests/common/java/android/net/NetworkStateSnapshotTest.kt
diff --git a/tests/net/common/java/android/net/NetworkTest.java b/packages/Connectivity/tests/common/java/android/net/NetworkTest.java
similarity index 100%
rename from tests/net/common/java/android/net/NetworkTest.java
rename to packages/Connectivity/tests/common/java/android/net/NetworkTest.java
diff --git a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java b/packages/Connectivity/tests/common/java/android/net/OemNetworkPreferencesTest.java
similarity index 100%
rename from tests/net/common/java/android/net/OemNetworkPreferencesTest.java
rename to packages/Connectivity/tests/common/java/android/net/OemNetworkPreferencesTest.java
diff --git a/tests/net/common/java/android/net/RouteInfoTest.java b/packages/Connectivity/tests/common/java/android/net/RouteInfoTest.java
similarity index 100%
rename from tests/net/common/java/android/net/RouteInfoTest.java
rename to packages/Connectivity/tests/common/java/android/net/RouteInfoTest.java
diff --git a/tests/net/common/java/android/net/StaticIpConfigurationTest.java b/packages/Connectivity/tests/common/java/android/net/StaticIpConfigurationTest.java
similarity index 100%
rename from tests/net/common/java/android/net/StaticIpConfigurationTest.java
rename to packages/Connectivity/tests/common/java/android/net/StaticIpConfigurationTest.java
diff --git a/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt b/packages/Connectivity/tests/common/java/android/net/TcpKeepalivePacketDataTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt
rename to packages/Connectivity/tests/common/java/android/net/TcpKeepalivePacketDataTest.kt
diff --git a/tests/net/common/java/android/net/UidRangeTest.java b/packages/Connectivity/tests/common/java/android/net/UidRangeTest.java
similarity index 100%
rename from tests/net/common/java/android/net/UidRangeTest.java
rename to packages/Connectivity/tests/common/java/android/net/UidRangeTest.java
diff --git a/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt b/packages/Connectivity/tests/common/java/android/net/UnderlyingNetworkInfoTest.kt
similarity index 80%
rename from tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt
rename to packages/Connectivity/tests/common/java/android/net/UnderlyingNetworkInfoTest.kt
index 87cfb34..f23ba26 100644
--- a/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt
+++ b/packages/Connectivity/tests/common/java/android/net/UnderlyingNetworkInfoTest.kt
@@ -36,15 +36,15 @@
     @Test
     fun testParcelUnparcel() {
         val testInfo = UnderlyingNetworkInfo(TEST_OWNER_UID, TEST_IFACE, TEST_IFACE_LIST)
-        assertEquals(TEST_OWNER_UID, testInfo.ownerUid)
-        assertEquals(TEST_IFACE, testInfo.iface)
-        assertEquals(TEST_IFACE_LIST, testInfo.underlyingIfaces)
+        assertEquals(TEST_OWNER_UID, testInfo.getOwnerUid())
+        assertEquals(TEST_IFACE, testInfo.getInterface())
+        assertEquals(TEST_IFACE_LIST, testInfo.getUnderlyingInterfaces())
         assertParcelSane(testInfo, 3)
 
         val emptyInfo = UnderlyingNetworkInfo(0, String(), listOf())
-        assertEquals(0, emptyInfo.ownerUid)
-        assertEquals(String(), emptyInfo.iface)
-        assertEquals(listOf(), emptyInfo.underlyingIfaces)
+        assertEquals(0, emptyInfo.getOwnerUid())
+        assertEquals(String(), emptyInfo.getInterface())
+        assertEquals(listOf(), emptyInfo.getUnderlyingInterfaces())
         assertParcelSane(emptyInfo, 3)
     }
 }
\ No newline at end of file
diff --git a/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java b/packages/Connectivity/tests/common/java/android/net/apf/ApfCapabilitiesTest.java
similarity index 100%
rename from tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java
rename to packages/Connectivity/tests/common/java/android/net/apf/ApfCapabilitiesTest.java
diff --git a/tests/net/common/java/android/net/metrics/ApfProgramEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/ApfProgramEventTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/metrics/ApfProgramEventTest.kt
rename to packages/Connectivity/tests/common/java/android/net/metrics/ApfProgramEventTest.kt
diff --git a/tests/net/common/java/android/net/metrics/ApfStatsTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/ApfStatsTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/metrics/ApfStatsTest.kt
rename to packages/Connectivity/tests/common/java/android/net/metrics/ApfStatsTest.kt
diff --git a/tests/net/common/java/android/net/metrics/DhcpClientEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/DhcpClientEventTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/metrics/DhcpClientEventTest.kt
rename to packages/Connectivity/tests/common/java/android/net/metrics/DhcpClientEventTest.kt
diff --git a/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/DhcpErrorEventTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt
rename to packages/Connectivity/tests/common/java/android/net/metrics/DhcpErrorEventTest.kt
diff --git a/tests/net/common/java/android/net/metrics/IpConnectivityLogTest.java b/packages/Connectivity/tests/common/java/android/net/metrics/IpConnectivityLogTest.java
similarity index 100%
rename from tests/net/common/java/android/net/metrics/IpConnectivityLogTest.java
rename to packages/Connectivity/tests/common/java/android/net/metrics/IpConnectivityLogTest.java
diff --git a/tests/net/common/java/android/net/metrics/IpManagerEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/IpManagerEventTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/metrics/IpManagerEventTest.kt
rename to packages/Connectivity/tests/common/java/android/net/metrics/IpManagerEventTest.kt
diff --git a/tests/net/common/java/android/net/metrics/IpReachabilityEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/IpReachabilityEventTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/metrics/IpReachabilityEventTest.kt
rename to packages/Connectivity/tests/common/java/android/net/metrics/IpReachabilityEventTest.kt
diff --git a/tests/net/common/java/android/net/metrics/NetworkEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/NetworkEventTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/metrics/NetworkEventTest.kt
rename to packages/Connectivity/tests/common/java/android/net/metrics/NetworkEventTest.kt
diff --git a/tests/net/common/java/android/net/metrics/RaEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/RaEventTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/metrics/RaEventTest.kt
rename to packages/Connectivity/tests/common/java/android/net/metrics/RaEventTest.kt
diff --git a/tests/net/common/java/android/net/metrics/ValidationProbeEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/ValidationProbeEventTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/metrics/ValidationProbeEventTest.kt
rename to packages/Connectivity/tests/common/java/android/net/metrics/ValidationProbeEventTest.kt
diff --git a/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt b/packages/Connectivity/tests/common/java/android/net/netstats/NetworkStatsApiTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt
rename to packages/Connectivity/tests/common/java/android/net/netstats/NetworkStatsApiTest.kt
diff --git a/tests/net/common/java/android/net/util/SocketUtilsTest.kt b/packages/Connectivity/tests/common/java/android/net/util/SocketUtilsTest.kt
similarity index 100%
rename from tests/net/common/java/android/net/util/SocketUtilsTest.kt
rename to packages/Connectivity/tests/common/java/android/net/util/SocketUtilsTest.kt
diff --git a/tests/net/deflake/Android.bp b/packages/Connectivity/tests/deflake/Android.bp
similarity index 100%
rename from tests/net/deflake/Android.bp
rename to packages/Connectivity/tests/deflake/Android.bp
diff --git a/tests/net/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt b/packages/Connectivity/tests/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt
similarity index 100%
rename from tests/net/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt
rename to packages/Connectivity/tests/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt
diff --git a/tests/net/integration/Android.bp b/packages/Connectivity/tests/integration/Android.bp
similarity index 100%
rename from tests/net/integration/Android.bp
rename to packages/Connectivity/tests/integration/Android.bp
diff --git a/tests/net/integration/AndroidManifest.xml b/packages/Connectivity/tests/integration/AndroidManifest.xml
similarity index 100%
rename from tests/net/integration/AndroidManifest.xml
rename to packages/Connectivity/tests/integration/AndroidManifest.xml
diff --git a/tests/net/integration/res/values/config.xml b/packages/Connectivity/tests/integration/res/values/config.xml
similarity index 100%
rename from tests/net/integration/res/values/config.xml
rename to packages/Connectivity/tests/integration/res/values/config.xml
diff --git a/tests/net/integration/src/android/net/TestNetworkStackClient.kt b/packages/Connectivity/tests/integration/src/android/net/TestNetworkStackClient.kt
similarity index 100%
rename from tests/net/integration/src/android/net/TestNetworkStackClient.kt
rename to packages/Connectivity/tests/integration/src/android/net/TestNetworkStackClient.kt
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
similarity index 100%
rename from tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
rename to packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl
similarity index 100%
rename from tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl
rename to packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.kt
similarity index 100%
rename from tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt
rename to packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.kt
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl
similarity index 100%
rename from tests/net/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl
rename to packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
similarity index 100%
rename from tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
rename to packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
similarity index 97%
rename from tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
rename to packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
index a44ad1e..eff6658 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
+++ b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
@@ -61,7 +61,6 @@
     private class NetworkMonitorDeps(private val privateDnsBypassNetwork: Network) :
             NetworkMonitor.Dependencies() {
         override fun getPrivateDnsBypassNetwork(network: Network?) = privateDnsBypassNetwork
-        override fun sendNetworkConditionsBroadcast(context: Context, broadcast: Intent) = Unit
     }
 
     private inner class TestNetworkStackConnector(context: Context) : NetworkStackConnector(
@@ -98,4 +97,4 @@
             cb.onNetworkMonitorCreated(NetworkMonitorConnector(nm, TestPermissionChecker()))
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt b/packages/Connectivity/tests/integration/util/com/android/server/ConnectivityServiceTestUtils.kt
similarity index 100%
rename from tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt
rename to packages/Connectivity/tests/integration/util/com/android/server/ConnectivityServiceTestUtils.kt
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/packages/Connectivity/tests/integration/util/com/android/server/NetworkAgentWrapper.java
similarity index 98%
rename from tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
rename to packages/Connectivity/tests/integration/util/com/android/server/NetworkAgentWrapper.java
index e2d43cb..e809550 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/packages/Connectivity/tests/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -370,4 +370,8 @@
             }
         }
     }
+
+    public boolean isBypassableVpn() {
+        return mNetworkAgentConfig.isBypassableVpn();
+    }
 }
diff --git a/tests/net/integration/util/com/android/server/TestNetIdManager.kt b/packages/Connectivity/tests/integration/util/com/android/server/TestNetIdManager.kt
similarity index 100%
rename from tests/net/integration/util/com/android/server/TestNetIdManager.kt
rename to packages/Connectivity/tests/integration/util/com/android/server/TestNetIdManager.kt
diff --git a/tests/net/smoketest/Android.bp b/packages/Connectivity/tests/smoketest/Android.bp
similarity index 100%
rename from tests/net/smoketest/Android.bp
rename to packages/Connectivity/tests/smoketest/Android.bp
diff --git a/tests/net/smoketest/AndroidManifest.xml b/packages/Connectivity/tests/smoketest/AndroidManifest.xml
similarity index 100%
rename from tests/net/smoketest/AndroidManifest.xml
rename to packages/Connectivity/tests/smoketest/AndroidManifest.xml
diff --git a/tests/net/smoketest/AndroidTest.xml b/packages/Connectivity/tests/smoketest/AndroidTest.xml
similarity index 100%
rename from tests/net/smoketest/AndroidTest.xml
rename to packages/Connectivity/tests/smoketest/AndroidTest.xml
diff --git a/tests/net/smoketest/java/SmokeTest.java b/packages/Connectivity/tests/smoketest/java/SmokeTest.java
similarity index 100%
rename from tests/net/smoketest/java/SmokeTest.java
rename to packages/Connectivity/tests/smoketest/java/SmokeTest.java
diff --git a/tests/net/Android.bp b/packages/Connectivity/tests/unit/Android.bp
similarity index 100%
rename from tests/net/Android.bp
rename to packages/Connectivity/tests/unit/Android.bp
diff --git a/tests/net/AndroidManifest.xml b/packages/Connectivity/tests/unit/AndroidManifest.xml
similarity index 100%
rename from tests/net/AndroidManifest.xml
rename to packages/Connectivity/tests/unit/AndroidManifest.xml
diff --git a/tests/net/AndroidTest.xml b/packages/Connectivity/tests/unit/AndroidTest.xml
similarity index 100%
rename from tests/net/AndroidTest.xml
rename to packages/Connectivity/tests/unit/AndroidTest.xml
diff --git a/tests/net/jarjar-rules.txt b/packages/Connectivity/tests/unit/jarjar-rules.txt
similarity index 100%
rename from tests/net/jarjar-rules.txt
rename to packages/Connectivity/tests/unit/jarjar-rules.txt
diff --git a/tests/net/java/android/app/usage/NetworkStatsManagerTest.java b/packages/Connectivity/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
similarity index 100%
rename from tests/net/java/android/app/usage/NetworkStatsManagerTest.java
rename to packages/Connectivity/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/packages/Connectivity/tests/unit/java/android/net/ConnectivityDiagnosticsManagerTest.java
similarity index 100%
rename from tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
rename to packages/Connectivity/tests/unit/java/android/net/ConnectivityDiagnosticsManagerTest.java
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/packages/Connectivity/tests/unit/java/android/net/ConnectivityManagerTest.java
similarity index 100%
rename from tests/net/java/android/net/ConnectivityManagerTest.java
rename to packages/Connectivity/tests/unit/java/android/net/ConnectivityManagerTest.java
diff --git a/tests/net/java/android/net/Ikev2VpnProfileTest.java b/packages/Connectivity/tests/unit/java/android/net/Ikev2VpnProfileTest.java
similarity index 100%
rename from tests/net/java/android/net/Ikev2VpnProfileTest.java
rename to packages/Connectivity/tests/unit/java/android/net/Ikev2VpnProfileTest.java
diff --git a/tests/net/java/android/net/IpMemoryStoreTest.java b/packages/Connectivity/tests/unit/java/android/net/IpMemoryStoreTest.java
similarity index 100%
rename from tests/net/java/android/net/IpMemoryStoreTest.java
rename to packages/Connectivity/tests/unit/java/android/net/IpMemoryStoreTest.java
diff --git a/tests/net/java/android/net/IpSecAlgorithmTest.java b/packages/Connectivity/tests/unit/java/android/net/IpSecAlgorithmTest.java
similarity index 100%
rename from tests/net/java/android/net/IpSecAlgorithmTest.java
rename to packages/Connectivity/tests/unit/java/android/net/IpSecAlgorithmTest.java
diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/packages/Connectivity/tests/unit/java/android/net/IpSecConfigTest.java
similarity index 100%
rename from tests/net/java/android/net/IpSecConfigTest.java
rename to packages/Connectivity/tests/unit/java/android/net/IpSecConfigTest.java
diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/packages/Connectivity/tests/unit/java/android/net/IpSecManagerTest.java
similarity index 100%
rename from tests/net/java/android/net/IpSecManagerTest.java
rename to packages/Connectivity/tests/unit/java/android/net/IpSecManagerTest.java
diff --git a/tests/net/java/android/net/IpSecTransformTest.java b/packages/Connectivity/tests/unit/java/android/net/IpSecTransformTest.java
similarity index 100%
rename from tests/net/java/android/net/IpSecTransformTest.java
rename to packages/Connectivity/tests/unit/java/android/net/IpSecTransformTest.java
diff --git a/tests/net/java/android/net/KeepalivePacketDataUtilTest.java b/packages/Connectivity/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java
similarity index 100%
rename from tests/net/java/android/net/KeepalivePacketDataUtilTest.java
rename to packages/Connectivity/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java
diff --git a/tests/net/java/android/net/MacAddressTest.java b/packages/Connectivity/tests/unit/java/android/net/MacAddressTest.java
similarity index 100%
rename from tests/net/java/android/net/MacAddressTest.java
rename to packages/Connectivity/tests/unit/java/android/net/MacAddressTest.java
diff --git a/tests/net/java/android/net/NetworkIdentityTest.kt b/packages/Connectivity/tests/unit/java/android/net/NetworkIdentityTest.kt
similarity index 100%
rename from tests/net/java/android/net/NetworkIdentityTest.kt
rename to packages/Connectivity/tests/unit/java/android/net/NetworkIdentityTest.kt
diff --git a/tests/net/java/android/net/NetworkStatsHistoryTest.java b/packages/Connectivity/tests/unit/java/android/net/NetworkStatsHistoryTest.java
similarity index 100%
rename from tests/net/java/android/net/NetworkStatsHistoryTest.java
rename to packages/Connectivity/tests/unit/java/android/net/NetworkStatsHistoryTest.java
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/packages/Connectivity/tests/unit/java/android/net/NetworkStatsTest.java
similarity index 100%
rename from tests/net/java/android/net/NetworkStatsTest.java
rename to packages/Connectivity/tests/unit/java/android/net/NetworkStatsTest.java
diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt
similarity index 100%
rename from tests/net/java/android/net/NetworkTemplateTest.kt
rename to packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt
diff --git a/tests/net/java/android/net/NetworkUtilsTest.java b/packages/Connectivity/tests/unit/java/android/net/NetworkUtilsTest.java
similarity index 100%
rename from tests/net/java/android/net/NetworkUtilsTest.java
rename to packages/Connectivity/tests/unit/java/android/net/NetworkUtilsTest.java
diff --git a/tests/net/java/android/net/QosSocketFilterTest.java b/packages/Connectivity/tests/unit/java/android/net/QosSocketFilterTest.java
similarity index 100%
rename from tests/net/java/android/net/QosSocketFilterTest.java
rename to packages/Connectivity/tests/unit/java/android/net/QosSocketFilterTest.java
diff --git a/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java b/packages/Connectivity/tests/unit/java/android/net/TelephonyNetworkSpecifierTest.java
similarity index 100%
rename from tests/net/java/android/net/TelephonyNetworkSpecifierTest.java
rename to packages/Connectivity/tests/unit/java/android/net/TelephonyNetworkSpecifierTest.java
diff --git a/tests/net/java/android/net/VpnManagerTest.java b/packages/Connectivity/tests/unit/java/android/net/VpnManagerTest.java
similarity index 85%
rename from tests/net/java/android/net/VpnManagerTest.java
rename to packages/Connectivity/tests/unit/java/android/net/VpnManagerTest.java
index c548e30..3135062 100644
--- a/tests/net/java/android/net/VpnManagerTest.java
+++ b/packages/Connectivity/tests/unit/java/android/net/VpnManagerTest.java
@@ -28,11 +28,13 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.test.mock.MockContext;
+import android.util.SparseArray;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.net.VpnProfile;
+import com.android.internal.util.MessageUtils;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -119,4 +121,18 @@
                 .setAuthPsk(PSK_BYTES)
                 .build();
     }
+
+    @Test
+    public void testVpnTypesEqual() throws Exception {
+        SparseArray<String> vmVpnTypes = MessageUtils.findMessageNames(
+                new Class[] { VpnManager.class }, new String[]{ "TYPE_VPN_" });
+        SparseArray<String> nativeVpnType = MessageUtils.findMessageNames(
+                new Class[] { NativeVpnType.class }, new String[]{ "" });
+
+        // TYPE_VPN_NONE = -1 is only defined in VpnManager.
+        assertEquals(vmVpnTypes.size() - 1, nativeVpnType.size());
+        for (int i = VpnManager.TYPE_VPN_SERVICE; i < vmVpnTypes.size(); i++) {
+            assertEquals(vmVpnTypes.get(i), "TYPE_VPN_" + nativeVpnType.get(i));
+        }
+    }
 }
diff --git a/tests/net/java/android/net/VpnTransportInfoTest.java b/packages/Connectivity/tests/unit/java/android/net/VpnTransportInfoTest.java
similarity index 100%
rename from tests/net/java/android/net/VpnTransportInfoTest.java
rename to packages/Connectivity/tests/unit/java/android/net/VpnTransportInfoTest.java
diff --git a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java b/packages/Connectivity/tests/unit/java/android/net/ipmemorystore/ParcelableTests.java
similarity index 100%
rename from tests/net/java/android/net/ipmemorystore/ParcelableTests.java
rename to packages/Connectivity/tests/unit/java/android/net/ipmemorystore/ParcelableTests.java
diff --git a/tests/net/java/android/net/nsd/NsdManagerTest.java b/packages/Connectivity/tests/unit/java/android/net/nsd/NsdManagerTest.java
similarity index 100%
rename from tests/net/java/android/net/nsd/NsdManagerTest.java
rename to packages/Connectivity/tests/unit/java/android/net/nsd/NsdManagerTest.java
diff --git a/tests/net/java/android/net/nsd/NsdServiceInfoTest.java b/packages/Connectivity/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java
similarity index 100%
rename from tests/net/java/android/net/nsd/NsdServiceInfoTest.java
rename to packages/Connectivity/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java
diff --git a/tests/net/java/android/net/util/DnsUtilsTest.java b/packages/Connectivity/tests/unit/java/android/net/util/DnsUtilsTest.java
similarity index 100%
rename from tests/net/java/android/net/util/DnsUtilsTest.java
rename to packages/Connectivity/tests/unit/java/android/net/util/DnsUtilsTest.java
diff --git a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt b/packages/Connectivity/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt
similarity index 100%
rename from tests/net/java/android/net/util/KeepaliveUtilsTest.kt
rename to packages/Connectivity/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt
diff --git a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt b/packages/Connectivity/tests/unit/java/android/net/util/MultinetworkPolicyTrackerTest.kt
similarity index 100%
rename from tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt
rename to packages/Connectivity/tests/unit/java/android/net/util/MultinetworkPolicyTrackerTest.kt
diff --git a/tests/net/java/com/android/internal/net/NetworkUtilsInternalTest.java b/packages/Connectivity/tests/unit/java/com/android/internal/net/NetworkUtilsInternalTest.java
similarity index 100%
rename from tests/net/java/com/android/internal/net/NetworkUtilsInternalTest.java
rename to packages/Connectivity/tests/unit/java/com/android/internal/net/NetworkUtilsInternalTest.java
diff --git a/tests/net/java/com/android/internal/net/VpnProfileTest.java b/packages/Connectivity/tests/unit/java/com/android/internal/net/VpnProfileTest.java
similarity index 100%
rename from tests/net/java/com/android/internal/net/VpnProfileTest.java
rename to packages/Connectivity/tests/unit/java/com/android/internal/net/VpnProfileTest.java
diff --git a/tests/net/java/com/android/internal/util/BitUtilsTest.java b/packages/Connectivity/tests/unit/java/com/android/internal/util/BitUtilsTest.java
similarity index 100%
rename from tests/net/java/com/android/internal/util/BitUtilsTest.java
rename to packages/Connectivity/tests/unit/java/com/android/internal/util/BitUtilsTest.java
diff --git a/tests/net/java/com/android/internal/util/RingBufferTest.java b/packages/Connectivity/tests/unit/java/com/android/internal/util/RingBufferTest.java
similarity index 100%
rename from tests/net/java/com/android/internal/util/RingBufferTest.java
rename to packages/Connectivity/tests/unit/java/com/android/internal/util/RingBufferTest.java
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java
similarity index 97%
rename from tests/net/java/com/android/server/ConnectivityServiceTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index bc06a6e..41458f1 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.CHANGE_NETWORK_STATE;
 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
 import static android.Manifest.permission.DUMP;
+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.app.PendingIntent.FLAG_IMMUTABLE;
@@ -44,9 +45,6 @@
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
 import static android.net.ConnectivityManager.EXTRA_NETWORK_TYPE;
-import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
-import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
-import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
 import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
 import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
 import static android.net.ConnectivityManager.TYPE_ETHERNET;
@@ -57,6 +55,9 @@
 import static android.net.ConnectivityManager.TYPE_PROXY;
 import static android.net.ConnectivityManager.TYPE_VPN;
 import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF;
+import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
+import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP;
@@ -126,6 +127,7 @@
 import static com.android.testutils.MiscAsserts.assertEmpty;
 import static com.android.testutils.MiscAsserts.assertLength;
 import static com.android.testutils.MiscAsserts.assertRunsInAtMost;
+import static com.android.testutils.MiscAsserts.assertSameElements;
 import static com.android.testutils.MiscAsserts.assertThrows;
 
 import static org.junit.Assert.assertEquals;
@@ -211,6 +213,8 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.MatchAllNetworkSpecifier;
+import android.net.NativeNetworkConfig;
+import android.net.NativeNetworkType;
 import android.net.Network;
 import android.net.NetworkAgent;
 import android.net.NetworkAgentConfig;
@@ -1253,6 +1257,8 @@
             verify(mMockNetd, never())
                     .networkRemoveUidRanges(eq(mMockVpn.getNetwork().getNetId()), any());
             mAgentRegistered = true;
+            verify(mMockNetd).networkCreate(nativeNetworkConfigVpn(getNetwork().netId,
+                    !mMockNetworkAgent.isBypassableVpn(), mVpnType));
             updateState(NetworkInfo.DetailedState.CONNECTED, "registerAgent");
             mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
             mNetworkAgent = mMockNetworkAgent.getNetworkAgent();
@@ -2809,8 +2815,9 @@
 
     private void grantUsingBackgroundNetworksPermissionForUid(
             final int uid, final String packageName) throws Exception {
-        when(mPackageManager.getPackageInfo(eq(packageName), eq(GET_PERMISSIONS)))
-                .thenReturn(buildPackageInfo(true, uid));
+        when(mPackageManager.getPackageInfo(
+                eq(packageName), eq(GET_PERMISSIONS | MATCH_ANY_USER)))
+                .thenReturn(buildPackageInfo(true /* hasSystemPermission */, uid));
         mService.mPermissionMonitor.onPackageAdded(packageName, uid);
     }
 
@@ -2860,6 +2867,16 @@
         mCm.unregisterNetworkCallback(callback);
     }
 
+    private NativeNetworkConfig nativeNetworkConfigPhysical(int netId, int permission) {
+        return new NativeNetworkConfig(netId, NativeNetworkType.PHYSICAL, permission,
+                /*secure=*/ false, VpnManager.TYPE_VPN_NONE);
+    }
+
+    private NativeNetworkConfig nativeNetworkConfigVpn(int netId, boolean secure, int vpnType) {
+        return new NativeNetworkConfig(netId, NativeNetworkType.VIRTUAL, INetd.PERMISSION_NONE,
+                secure, vpnType);
+    }
+
     @Test
     public void testNetworkAgentCallbacks() throws Exception {
         // Keeps track of the order of events that happen in this test.
@@ -2881,8 +2898,8 @@
             wifiNetwork.set(mWiFiNetworkAgent.getNetwork());
             assertNotNull(wifiNetwork.get());
             try {
-                verify(mMockNetd).networkCreatePhysical(wifiNetwork.get().getNetId(),
-                        INetd.PERMISSION_NONE);
+                verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+                        wifiNetwork.get().getNetId(), INetd.PERMISSION_NONE));
             } catch (RemoteException impossible) {
                 fail();
             }
@@ -4242,10 +4259,9 @@
         waitForIdle();
     }
 
-    private void setPrivateDnsSettings(String mode, String specifier) {
-        final ContentResolver cr = mServiceContext.getContentResolver();
-        Settings.Global.putString(cr, ConnectivitySettingsManager.PRIVATE_DNS_MODE, mode);
-        Settings.Global.putString(cr, ConnectivitySettingsManager.PRIVATE_DNS_SPECIFIER, specifier);
+    private void setPrivateDnsSettings(int mode, String specifier) {
+        ConnectivitySettingsManager.setPrivateDnsMode(mServiceContext, mode);
+        ConnectivitySettingsManager.setPrivateDnsHostname(mServiceContext, specifier);
         mService.updatePrivateDnsSettings();
         waitForIdle();
     }
@@ -5791,20 +5807,8 @@
         mCm.unregisterNetworkCallback(networkCallback);
     }
 
-    private <T> void assertSameElementsNoDuplicates(T[] expected, T[] actual) {
-        // Easier to implement than a proper "assertSameElements" method that also correctly deals
-        // with duplicates.
-        final String msg = Arrays.toString(expected) + " != " + Arrays.toString(actual);
-        assertEquals(msg, expected.length, actual.length);
-        Set expectedSet = new ArraySet<>(Arrays.asList(expected));
-        assertEquals("expected contains duplicates", expectedSet.size(), expected.length);
-        // actual cannot have duplicates because it's the same length and has the same elements.
-        Set actualSet = new ArraySet<>(Arrays.asList(actual));
-        assertEquals(expectedSet, actualSet);
-    }
-
-    private void expectNetworkStatus(Network[] networks, String defaultIface,
-            Integer vpnUid, String vpnIfname, String[] underlyingIfaces) throws Exception {
+    private void expectNotifyNetworkStatus(List<Network> networks, String defaultIface,
+            Integer vpnUid, String vpnIfname, List<String> underlyingIfaces) throws Exception {
         ArgumentCaptor<List<Network>> networksCaptor = ArgumentCaptor.forClass(List.class);
         ArgumentCaptor<List<UnderlyingNetworkInfo>> vpnInfosCaptor =
                 ArgumentCaptor.forClass(List.class);
@@ -5812,26 +5816,24 @@
         verify(mStatsManager, atLeastOnce()).notifyNetworkStatus(networksCaptor.capture(),
                 any(List.class), eq(defaultIface), vpnInfosCaptor.capture());
 
-        assertSameElementsNoDuplicates(networksCaptor.getValue().toArray(), networks);
+        assertSameElements(networksCaptor.getValue(), networks);
 
-        UnderlyingNetworkInfo[] infos =
-                vpnInfosCaptor.getValue().toArray(new UnderlyingNetworkInfo[0]);
+        List<UnderlyingNetworkInfo> infos = vpnInfosCaptor.getValue();
         if (vpnUid != null) {
-            assertEquals("Should have exactly one VPN:", 1, infos.length);
-            UnderlyingNetworkInfo info = infos[0];
+            assertEquals("Should have exactly one VPN:", 1, infos.size());
+            UnderlyingNetworkInfo info = infos.get(0);
             assertEquals("Unexpected VPN owner:", (int) vpnUid, info.getOwnerUid());
-            assertEquals("Unexpected VPN interface:", vpnIfname, info.getIface());
-            assertSameElementsNoDuplicates(underlyingIfaces,
-                    info.getUnderlyingIfaces().toArray(new String[0]));
+            assertEquals("Unexpected VPN interface:", vpnIfname, info.getInterface());
+            assertSameElements(underlyingIfaces, info.getUnderlyingInterfaces());
         } else {
-            assertEquals(0, infos.length);
+            assertEquals(0, infos.size());
             return;
         }
     }
 
-    private void expectNetworkStatus(
-            Network[] networks, String defaultIface) throws Exception {
-        expectNetworkStatus(networks, defaultIface, null, null, new String[0]);
+    private void expectNotifyNetworkStatus(
+            List<Network> networks, String defaultIface) throws Exception {
+        expectNotifyNetworkStatus(networks, defaultIface, null, null, List.of());
     }
 
     @Test
@@ -5839,8 +5841,8 @@
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
 
-        final Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()};
-        final Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()};
+        final List<Network> onlyCell = List.of(mCellNetworkAgent.getNetwork());
+        final List<Network> onlyWifi = List.of(mWiFiNetworkAgent.getNetwork());
 
         LinkProperties cellLp = new LinkProperties();
         cellLp.setInterfaceName(MOBILE_IFNAME);
@@ -5851,7 +5853,7 @@
         mCellNetworkAgent.connect(false);
         mCellNetworkAgent.sendLinkProperties(cellLp);
         waitForIdle();
-        expectNetworkStatus(onlyCell, MOBILE_IFNAME);
+        expectNotifyNetworkStatus(onlyCell, MOBILE_IFNAME);
         reset(mStatsManager);
 
         // Default network switch should update ifaces.
@@ -5859,37 +5861,37 @@
         mWiFiNetworkAgent.sendLinkProperties(wifiLp);
         waitForIdle();
         assertEquals(wifiLp, mService.getActiveLinkProperties());
-        expectNetworkStatus(onlyWifi, WIFI_IFNAME);
+        expectNotifyNetworkStatus(onlyWifi, WIFI_IFNAME);
         reset(mStatsManager);
 
         // Disconnect should update ifaces.
         mWiFiNetworkAgent.disconnect();
         waitForIdle();
-        expectNetworkStatus(onlyCell, MOBILE_IFNAME);
+        expectNotifyNetworkStatus(onlyCell, MOBILE_IFNAME);
         reset(mStatsManager);
 
         // Metered change should update ifaces
         mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
         waitForIdle();
-        expectNetworkStatus(onlyCell, MOBILE_IFNAME);
+        expectNotifyNetworkStatus(onlyCell, MOBILE_IFNAME);
         reset(mStatsManager);
 
         mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
         waitForIdle();
-        expectNetworkStatus(onlyCell, MOBILE_IFNAME);
+        expectNotifyNetworkStatus(onlyCell, MOBILE_IFNAME);
         reset(mStatsManager);
 
         // Temp metered change shouldn't update ifaces
         mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
         waitForIdle();
-        verify(mStatsManager, never()).notifyNetworkStatus(eq(Arrays.asList(onlyCell)),
+        verify(mStatsManager, never()).notifyNetworkStatus(eq(onlyCell),
                 any(List.class), eq(MOBILE_IFNAME), any(List.class));
         reset(mStatsManager);
 
         // Roaming change should update ifaces
         mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
         waitForIdle();
-        expectNetworkStatus(onlyCell, MOBILE_IFNAME);
+        expectNotifyNetworkStatus(onlyCell, MOBILE_IFNAME);
         reset(mStatsManager);
 
         // Test VPNs.
@@ -5899,29 +5901,29 @@
         mMockVpn.establishForMyUid(lp);
         assertUidRangesUpdatedForMyUid(true);
 
-        final Network[] cellAndVpn = new Network[] {
-                mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork()};
+        final List<Network> cellAndVpn =
+                List.of(mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork());
 
         // A VPN with default (null) underlying networks sets the underlying network's interfaces...
-        expectNetworkStatus(cellAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
-                new String[]{MOBILE_IFNAME});
+        expectNotifyNetworkStatus(cellAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+                List.of(MOBILE_IFNAME));
 
         // ...and updates them as the default network switches.
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
         mWiFiNetworkAgent.sendLinkProperties(wifiLp);
         final Network[] onlyNull = new Network[]{null};
-        final Network[] wifiAndVpn = new Network[] {
-                mWiFiNetworkAgent.getNetwork(), mMockVpn.getNetwork()};
-        final Network[] cellAndWifi = new Network[] {
-                mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()};
-        final Network[] cellNullAndWifi = new Network[] {
-                mCellNetworkAgent.getNetwork(), null, mWiFiNetworkAgent.getNetwork()};
+        final List<Network> wifiAndVpn =
+                List.of(mWiFiNetworkAgent.getNetwork(), mMockVpn.getNetwork());
+        final List<Network> cellAndWifi =
+                List.of(mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork());
+        final Network[] cellNullAndWifi =
+                new Network[]{mCellNetworkAgent.getNetwork(), null, mWiFiNetworkAgent.getNetwork()};
 
         waitForIdle();
         assertEquals(wifiLp, mService.getActiveLinkProperties());
-        expectNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
-                new String[]{WIFI_IFNAME});
+        expectNotifyNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
+                List.of(WIFI_IFNAME));
         reset(mStatsManager);
 
         // A VPN that sets its underlying networks passes the underlying interfaces, and influences
@@ -5930,23 +5932,23 @@
         // MOBILE_IFNAME even though the default network is wifi.
         // TODO: fix this to pass in the actual default network interface. Whether or not the VPN
         // applies to the system server UID should not have any bearing on network stats.
-        mMockVpn.setUnderlyingNetworks(onlyCell);
+        mMockVpn.setUnderlyingNetworks(onlyCell.toArray(new Network[0]));
         waitForIdle();
-        expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
-                new String[]{MOBILE_IFNAME});
+        expectNotifyNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+                List.of(MOBILE_IFNAME));
         reset(mStatsManager);
 
-        mMockVpn.setUnderlyingNetworks(cellAndWifi);
+        mMockVpn.setUnderlyingNetworks(cellAndWifi.toArray(new Network[0]));
         waitForIdle();
-        expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
-                new String[]{MOBILE_IFNAME, WIFI_IFNAME});
+        expectNotifyNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+                List.of(MOBILE_IFNAME, WIFI_IFNAME));
         reset(mStatsManager);
 
         // Null underlying networks are ignored.
         mMockVpn.setUnderlyingNetworks(cellNullAndWifi);
         waitForIdle();
-        expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
-                new String[]{MOBILE_IFNAME, WIFI_IFNAME});
+        expectNotifyNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+                List.of(MOBILE_IFNAME, WIFI_IFNAME));
         reset(mStatsManager);
 
         // If an underlying network disconnects, that interface should no longer be underlying.
@@ -5959,15 +5961,15 @@
         mCellNetworkAgent.disconnect();
         waitForIdle();
         assertNull(mService.getLinkProperties(mCellNetworkAgent.getNetwork()));
-        expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
-                new String[]{MOBILE_IFNAME, WIFI_IFNAME});
+        expectNotifyNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+                List.of(MOBILE_IFNAME, WIFI_IFNAME));
 
         // Confirm that we never tell NetworkStatsService that cell is no longer the underlying
         // network for the VPN...
         verify(mStatsManager, never()).notifyNetworkStatus(any(List.class),
                 any(List.class), any() /* anyString() doesn't match null */,
-                argThat(infos -> infos.get(0).getUnderlyingIfaces().size() == 1
-                        && WIFI_IFNAME.equals(infos.get(0).getUnderlyingIfaces().get(0))));
+                argThat(infos -> infos.get(0).getUnderlyingInterfaces().size() == 1
+                        && WIFI_IFNAME.equals(infos.get(0).getUnderlyingInterfaces().get(0))));
         verifyNoMoreInteractions(mStatsManager);
         reset(mStatsManager);
 
@@ -5980,8 +5982,8 @@
         waitForIdle();
         verify(mStatsManager).notifyNetworkStatus(any(List.class),
                 any(List.class), any() /* anyString() doesn't match null */,
-                argThat(vpnInfos -> vpnInfos.get(0).getUnderlyingIfaces().size() == 1
-                        && WIFI_IFNAME.equals(vpnInfos.get(0).getUnderlyingIfaces().get(0))));
+                argThat(vpnInfos -> vpnInfos.get(0).getUnderlyingInterfaces().size() == 1
+                        && WIFI_IFNAME.equals(vpnInfos.get(0).getUnderlyingInterfaces().get(0))));
         mEthernetNetworkAgent.disconnect();
         waitForIdle();
         reset(mStatsManager);
@@ -5994,26 +5996,26 @@
         // Also, for the same reason as above, the active interface passed in is null.
         mMockVpn.setUnderlyingNetworks(new Network[0]);
         waitForIdle();
-        expectNetworkStatus(wifiAndVpn, null);
+        expectNotifyNetworkStatus(wifiAndVpn, null);
         reset(mStatsManager);
 
         // Specifying only a null underlying network is the same as no networks.
         mMockVpn.setUnderlyingNetworks(onlyNull);
         waitForIdle();
-        expectNetworkStatus(wifiAndVpn, null);
+        expectNotifyNetworkStatus(wifiAndVpn, null);
         reset(mStatsManager);
 
         // Specifying networks that are all disconnected is the same as specifying no networks.
-        mMockVpn.setUnderlyingNetworks(onlyCell);
+        mMockVpn.setUnderlyingNetworks(onlyCell.toArray(new Network[0]));
         waitForIdle();
-        expectNetworkStatus(wifiAndVpn, null);
+        expectNotifyNetworkStatus(wifiAndVpn, null);
         reset(mStatsManager);
 
         // Passing in null again means follow the default network again.
         mMockVpn.setUnderlyingNetworks(null);
         waitForIdle();
-        expectNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
-                new String[]{WIFI_IFNAME});
+        expectNotifyNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
+                List.of(WIFI_IFNAME));
         reset(mStatsManager);
     }
 
@@ -8327,7 +8329,8 @@
         final int cellNetId = mCellNetworkAgent.getNetwork().netId;
         waitForIdle();
 
-        verify(mMockNetd, times(1)).networkCreatePhysical(eq(cellNetId), anyInt());
+        verify(mMockNetd, times(1)).networkCreate(nativeNetworkConfigPhysical(cellNetId,
+                INetd.PERMISSION_NONE));
         assertRoutesAdded(cellNetId, ipv6Subnet, defaultRoute);
         verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId));
         verify(mMockNetd, times(1)).networkAddInterface(cellNetId, MOBILE_IFNAME);
@@ -9392,9 +9395,9 @@
         @Override
         public TransportInfo makeCopy(@NetworkCapabilities.RedactionType long redactions) {
             return new TestTransportInfo(
-                    (redactions & REDACT_FOR_ACCESS_FINE_LOCATION) != 0,
-                    (redactions & REDACT_FOR_LOCAL_MAC_ADDRESS) != 0,
-                    (redactions & REDACT_FOR_NETWORK_SETTINGS) != 0
+                    locationRedacted | (redactions & REDACT_FOR_ACCESS_FINE_LOCATION) != 0,
+                    localMacAddressRedacted | (redactions & REDACT_FOR_LOCAL_MAC_ADDRESS) != 0,
+                    settingsRedacted | (redactions & REDACT_FOR_NETWORK_SETTINGS) != 0
             );
         }
 
@@ -9417,8 +9420,26 @@
         public int hashCode() {
             return Objects.hash(locationRedacted, localMacAddressRedacted, settingsRedacted);
         }
+
+        @Override
+        public String toString() {
+            return String.format(
+                    "TestTransportInfo{locationRedacted=%s macRedacted=%s settingsRedacted=%s}",
+                    locationRedacted, localMacAddressRedacted, settingsRedacted);
+        }
     }
 
+    private TestTransportInfo getTestTransportInfo(NetworkCapabilities nc) {
+        return (TestTransportInfo) nc.getTransportInfo();
+    }
+
+    private TestTransportInfo getTestTransportInfo(TestNetworkAgentWrapper n) {
+        final NetworkCapabilities nc = mCm.getNetworkCapabilities(n.getNetwork());
+        assertNotNull(nc);
+        return getTestTransportInfo(nc);
+    }
+
+
     private void verifyNetworkCallbackLocationDataInclusionUsingTransportInfoAndOwnerUidInNetCaps(
             @NonNull TestNetworkCallback wifiNetworkCallback, int actualOwnerUid,
             @NonNull TransportInfo actualTransportInfo, int expectedOwnerUid,
@@ -9447,7 +9468,6 @@
         wifiNetworkCallback.expectCapabilitiesThat(mWiFiNetworkAgent,
                 nc -> Objects.equals(expectedOwnerUid, nc.getOwnerUid())
                         && Objects.equals(expectedTransportInfo, nc.getTransportInfo()));
-
     }
 
     @Test
@@ -9468,6 +9488,40 @@
                 wifiNetworkCallack, ownerUid, transportInfo, INVALID_UID, sanitizedTransportInfo);
     }
 
+    @Test
+    public void testTransportInfoRedactionInSynchronousCalls() throws Exception {
+        final NetworkCapabilities ncTemplate = new NetworkCapabilities()
+                .addTransportType(TRANSPORT_WIFI)
+                .setTransportInfo(new TestTransportInfo());
+
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, new LinkProperties(),
+                ncTemplate);
+        mWiFiNetworkAgent.connect(true /* validated; waits for callback */);
+
+        // NETWORK_SETTINGS redaction is controlled by the NETWORK_SETTINGS permission
+        assertTrue(getTestTransportInfo(mWiFiNetworkAgent).settingsRedacted);
+        withPermission(NETWORK_SETTINGS, () -> {
+            assertFalse(getTestTransportInfo(mWiFiNetworkAgent).settingsRedacted);
+        });
+        assertTrue(getTestTransportInfo(mWiFiNetworkAgent).settingsRedacted);
+
+        // LOCAL_MAC_ADDRESS redaction is controlled by the LOCAL_MAC_ADDRESS permission
+        assertTrue(getTestTransportInfo(mWiFiNetworkAgent).localMacAddressRedacted);
+        withPermission(LOCAL_MAC_ADDRESS, () -> {
+            assertFalse(getTestTransportInfo(mWiFiNetworkAgent).localMacAddressRedacted);
+        });
+        assertTrue(getTestTransportInfo(mWiFiNetworkAgent).localMacAddressRedacted);
+
+        // Synchronous getNetworkCapabilities calls never return unredacted location-sensitive
+        // information.
+        assertTrue(getTestTransportInfo(mWiFiNetworkAgent).locationRedacted);
+        setupLocationPermissions(Build.VERSION_CODES.S, true, AppOpsManager.OPSTR_FINE_LOCATION,
+                Manifest.permission.ACCESS_FINE_LOCATION);
+        assertTrue(getTestTransportInfo(mWiFiNetworkAgent).locationRedacted);
+        denyAllLocationPrivilegedPermissions();
+        assertTrue(getTestTransportInfo(mWiFiNetworkAgent).locationRedacted);
+    }
+
     private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
             throws Exception {
         final Set<UidRange> vpnRange = Collections.singleton(PRIMARY_UIDRANGE);
@@ -9825,12 +9879,27 @@
         // Connect the cell agent verify that it notifies TestNetworkCallback that it is available
         final TestNetworkCallback callback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(callback);
-        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+
+        final NetworkCapabilities ncTemplate = new NetworkCapabilities()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .setTransportInfo(new TestTransportInfo());
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, new LinkProperties(),
+                ncTemplate);
         mCellNetworkAgent.connect(true);
         callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         callback.assertNoCallback();
     }
 
+    private boolean areConnDiagCapsRedacted(NetworkCapabilities nc) {
+        TestTransportInfo ti = (TestTransportInfo) nc.getTransportInfo();
+        return nc.getUids() == null
+                && nc.getAdministratorUids().length == 0
+                && nc.getOwnerUid() == Process.INVALID_UID
+                && getTestTransportInfo(nc).locationRedacted
+                && getTestTransportInfo(nc).localMacAddressRedacted
+                && getTestTransportInfo(nc).settingsRedacted;
+    }
+
     @Test
     public void testConnectivityDiagnosticsCallbackOnConnectivityReportAvailable()
             throws Exception {
@@ -9841,12 +9910,7 @@
 
         // Verify onConnectivityReport fired
         verify(mConnectivityDiagnosticsCallback).onConnectivityReportAvailable(
-                argThat(report -> {
-                    final NetworkCapabilities nc = report.getNetworkCapabilities();
-                    return nc.getUids() == null
-                            && nc.getAdministratorUids().length == 0
-                            && nc.getOwnerUid() == Process.INVALID_UID;
-                }));
+                argThat(report -> areConnDiagCapsRedacted(report.getNetworkCapabilities())));
     }
 
     @Test
@@ -9862,12 +9926,7 @@
 
         // Verify onDataStallSuspected fired
         verify(mConnectivityDiagnosticsCallback).onDataStallSuspected(
-                argThat(report -> {
-                    final NetworkCapabilities nc = report.getNetworkCapabilities();
-                    return nc.getUids() == null
-                            && nc.getAdministratorUids().length == 0
-                            && nc.getOwnerUid() == Process.INVALID_UID;
-                }));
+                argThat(report -> areConnDiagCapsRedacted(report.getNetworkCapabilities())));
     }
 
     @Test
@@ -11982,8 +12041,9 @@
         mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         mProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
-        inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
-                INetd.PERMISSION_NONE);
+        inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+                mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE));
+
 
         final TestOnCompleteListener listener = new TestOnCompleteListener();
         mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
@@ -12010,8 +12070,8 @@
         mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent);
         mSystemDefaultNetworkCallback.assertNoCallback();
         mDefaultNetworkCallback.assertNoCallback();
-        inOrder.verify(mMockNetd).networkCreatePhysical(workAgent.getNetwork().netId,
-                INetd.PERMISSION_SYSTEM);
+        inOrder.verify(mMockNetd).networkCreate(
+                nativeNetworkConfigPhysical(workAgent.getNetwork().netId, INetd.PERMISSION_SYSTEM));
         inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
                 uidRangeFor(testHandle));
         inOrder.verify(mMockNetd).networkRemoveUidRanges(mCellNetworkAgent.getNetwork().netId,
@@ -12054,8 +12114,8 @@
         mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         mProfileDefaultNetworkCallback.assertNoCallback();
-        inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
-                INetd.PERMISSION_NONE);
+        inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+                mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE));
 
         // When the agent disconnects, test that the app on the work profile falls back to the
         // default network.
@@ -12085,8 +12145,8 @@
 
         mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent2);
         assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
-        inOrder.verify(mMockNetd).networkCreatePhysical(workAgent2.getNetwork().netId,
-                INetd.PERMISSION_SYSTEM);
+        inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+                workAgent2.getNetwork().netId, INetd.PERMISSION_SYSTEM));
         inOrder.verify(mMockNetd).networkAddUidRanges(workAgent2.getNetwork().netId,
                 uidRangeFor(testHandle));
 
@@ -12131,8 +12191,8 @@
         mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
                 r -> r.run(), listener);
         listener.expectOnComplete();
-        inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
-                INetd.PERMISSION_NONE);
+        inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+                mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE));
         inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
                 uidRangeFor(testHandle));
 
@@ -12184,10 +12244,10 @@
         mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         mProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         app4Cb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
-        inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
-                INetd.PERMISSION_NONE);
-        inOrder.verify(mMockNetd).networkCreatePhysical(workAgent.getNetwork().netId,
-                INetd.PERMISSION_SYSTEM);
+        inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+                mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE));
+        inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+                workAgent.getNetwork().netId, INetd.PERMISSION_SYSTEM));
 
         final TestOnCompleteListener listener = new TestOnCompleteListener();
         mCm.setProfileNetworkPreference(testHandle2, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
@@ -12239,8 +12299,8 @@
         mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
                 r -> r.run(), listener);
         listener.expectOnComplete();
-        inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
-                INetd.PERMISSION_NONE);
+        inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+                mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE));
         inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId,
                 uidRangeFor(testHandle));
 
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
similarity index 100%
rename from tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
diff --git a/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java
similarity index 100%
rename from tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceTest.java
similarity index 100%
rename from tests/net/java/com/android/server/IpSecServiceTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceTest.java
diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/packages/Connectivity/tests/unit/java/com/android/server/LegacyTypeTrackerTest.kt
similarity index 100%
rename from tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
rename to packages/Connectivity/tests/unit/java/com/android/server/LegacyTypeTrackerTest.kt
diff --git a/tests/net/java/com/android/server/NetIdManagerTest.kt b/packages/Connectivity/tests/unit/java/com/android/server/NetIdManagerTest.kt
similarity index 100%
rename from tests/net/java/com/android/server/NetIdManagerTest.kt
rename to packages/Connectivity/tests/unit/java/com/android/server/NetIdManagerTest.kt
diff --git a/tests/net/java/com/android/server/NetworkManagementServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/NetworkManagementServiceTest.java
similarity index 100%
rename from tests/net/java/com/android/server/NetworkManagementServiceTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/NetworkManagementServiceTest.java
diff --git a/tests/net/java/com/android/server/NsdServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/NsdServiceTest.java
similarity index 100%
rename from tests/net/java/com/android/server/NsdServiceTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/NsdServiceTest.java
diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
similarity index 96%
rename from tests/net/java/com/android/server/connectivity/DnsManagerTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
index 692c50f..0ffeec9 100644
--- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
@@ -16,10 +16,10 @@
 
 package com.android.server.connectivity;
 
-import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
-import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
 import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_DEFAULT_MODE;
 import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE;
+import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF;
+import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
 import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_SPECIFIER;
 import static android.net.NetworkCapabilities.MAX_TRANSPORT;
 import static android.net.NetworkCapabilities.MIN_TRANSPORT;
@@ -44,6 +44,7 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.net.ConnectivitySettingsManager;
 import android.net.IDnsResolver;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
@@ -187,9 +188,8 @@
         lp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("2001:db8:1::1"),
                 TEST_IFACENAME));
 
-        Settings.Global.putString(mContentResolver,
-                PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
-        Settings.Global.putString(mContentResolver, PRIVATE_DNS_SPECIFIER, "strictmode.com");
+        ConnectivitySettingsManager.setPrivateDnsMode(mCtx, PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
+        ConnectivitySettingsManager.setPrivateDnsHostname(mCtx, "strictmode.com");
         mDnsManager.updatePrivateDns(new Network(TEST_NETID),
                 new PrivateDnsConfig("strictmode.com", new InetAddress[] {
                     InetAddress.parseNumericAddress("6.6.6.6"),
@@ -294,7 +294,7 @@
         assertNull(lp.getPrivateDnsServerName());
 
         // Turn private DNS mode off
-        Settings.Global.putString(mContentResolver, PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_OFF);
+        ConnectivitySettingsManager.setPrivateDnsMode(mCtx, PRIVATE_DNS_MODE_OFF);
         mDnsManager.updatePrivateDns(new Network(TEST_NETID),
                 mDnsManager.getPrivateDnsConfig());
         mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
@@ -318,16 +318,15 @@
         assertEquals(new InetAddress[0], cfgAuto.ips);
 
         // Pretend a gservices push sets the default to "off".
-        Settings.Global.putString(mContentResolver, PRIVATE_DNS_DEFAULT_MODE, "off");
+        ConnectivitySettingsManager.setPrivateDnsDefaultMode(mCtx, PRIVATE_DNS_MODE_OFF);
         final PrivateDnsConfig cfgOff = DnsManager.getPrivateDnsConfig(mCtx);
         assertFalse(cfgOff.useTls);
         assertEquals("", cfgOff.hostname);
         assertEquals(new InetAddress[0], cfgOff.ips);
 
         // Strict mode still works.
-        Settings.Global.putString(
-                mContentResolver, PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
-        Settings.Global.putString(mContentResolver, PRIVATE_DNS_SPECIFIER, "strictmode.com");
+        ConnectivitySettingsManager.setPrivateDnsMode(mCtx, PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
+        ConnectivitySettingsManager.setPrivateDnsHostname(mCtx, "strictmode.com");
         final PrivateDnsConfig cfgStrict = DnsManager.getPrivateDnsConfig(mCtx);
         assertTrue(cfgStrict.useTls);
         assertEquals("strictmode.com", cfgStrict.hostname);
diff --git a/tests/net/java/com/android/server/connectivity/FullScoreTest.kt b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/FullScoreTest.kt
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java
diff --git a/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/MetricsTestUtil.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/MetricsTestUtil.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/MetricsTestUtil.java
diff --git a/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
diff --git a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
diff --git a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
similarity index 97%
rename from tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
index d7535a9..02a5808 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -479,13 +479,14 @@
     public void testUidFilteringDuringVpnConnectDisconnectAndUidUpdates() throws Exception {
         when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
                 Arrays.asList(new PackageInfo[] {
-                        buildPackageInfo(/* SYSTEM */ true, SYSTEM_UID1, MOCK_USER1),
-                        buildPackageInfo(/* SYSTEM */ false, MOCK_UID1, MOCK_USER1),
-                        buildPackageInfo(/* SYSTEM */ false, MOCK_UID2, MOCK_USER1),
-                        buildPackageInfo(/* SYSTEM */ false, VPN_UID, MOCK_USER1)
+                        buildPackageInfo(true /* hasSystemPermission */, SYSTEM_UID1, MOCK_USER1),
+                        buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1),
+                        buildPackageInfo(false /* hasSystemPermission */, MOCK_UID2, MOCK_USER1),
+                        buildPackageInfo(false /* hasSystemPermission */, VPN_UID, MOCK_USER1)
                 }));
-        when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS))).thenReturn(
-                buildPackageInfo(false, MOCK_UID1, MOCK_USER1));
+        when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1),
+                eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
+                buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1));
         mPermissionMonitor.startMonitoring();
         // Every app on user 0 except MOCK_UID2 are under VPN.
         final Set<UidRange> vpnRange1 = new HashSet<>(Arrays.asList(new UidRange[] {
@@ -530,11 +531,12 @@
     public void testUidFilteringDuringPackageInstallAndUninstall() throws Exception {
         when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
                 Arrays.asList(new PackageInfo[] {
-                        buildPackageInfo(true, SYSTEM_UID1, MOCK_USER1),
-                        buildPackageInfo(false, VPN_UID, MOCK_USER1)
+                        buildPackageInfo(true /* hasSystemPermission */, SYSTEM_UID1, MOCK_USER1),
+                        buildPackageInfo(false /* hasSystemPermission */, VPN_UID, MOCK_USER1)
                 }));
-        when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS))).thenReturn(
-                        buildPackageInfo(false, MOCK_UID1, MOCK_USER1));
+        when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1),
+                eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
+                buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1));
 
         mPermissionMonitor.startMonitoring();
         final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(MOCK_USER1));
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/VpnTest.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/VpnTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/connectivity/VpnTest.java
diff --git a/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java
similarity index 100%
rename from tests/net/java/com/android/server/net/NetworkStatsAccessTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java
diff --git a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsBaseTest.java
similarity index 100%
rename from tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsBaseTest.java
diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java
similarity index 100%
rename from tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java
diff --git a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
similarity index 100%
rename from tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
diff --git a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
similarity index 100%
rename from tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
similarity index 100%
rename from tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
diff --git a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
similarity index 100%
rename from tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
diff --git a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
similarity index 100%
rename from tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
rename to packages/Connectivity/tests/unit/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
diff --git a/tests/net/jni/Android.bp b/packages/Connectivity/tests/unit/jni/Android.bp
similarity index 100%
rename from tests/net/jni/Android.bp
rename to packages/Connectivity/tests/unit/jni/Android.bp
diff --git a/tests/net/jni/test_onload.cpp b/packages/Connectivity/tests/unit/jni/test_onload.cpp
similarity index 100%
rename from tests/net/jni/test_onload.cpp
rename to packages/Connectivity/tests/unit/jni/test_onload.cpp
diff --git a/tests/net/res/raw/history_v1 b/packages/Connectivity/tests/unit/res/raw/history_v1
similarity index 100%
rename from tests/net/res/raw/history_v1
rename to packages/Connectivity/tests/unit/res/raw/history_v1
Binary files differ
diff --git a/tests/net/res/raw/net_dev_typical b/packages/Connectivity/tests/unit/res/raw/net_dev_typical
similarity index 100%
rename from tests/net/res/raw/net_dev_typical
rename to packages/Connectivity/tests/unit/res/raw/net_dev_typical
diff --git a/tests/net/res/raw/netstats_uid_v4 b/packages/Connectivity/tests/unit/res/raw/netstats_uid_v4
similarity index 100%
rename from tests/net/res/raw/netstats_uid_v4
rename to packages/Connectivity/tests/unit/res/raw/netstats_uid_v4
Binary files differ
diff --git a/tests/net/res/raw/netstats_v1 b/packages/Connectivity/tests/unit/res/raw/netstats_v1
similarity index 100%
rename from tests/net/res/raw/netstats_v1
rename to packages/Connectivity/tests/unit/res/raw/netstats_v1
Binary files differ
diff --git a/tests/net/res/raw/xt_qtaguid_iface_fmt_typical b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_iface_fmt_typical
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_iface_fmt_typical
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_iface_fmt_typical
diff --git a/tests/net/res/raw/xt_qtaguid_iface_typical b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_iface_typical
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_iface_typical
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_iface_typical
diff --git a/tests/net/res/raw/xt_qtaguid_typical b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_typical
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_typical
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_typical
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_incorrect_iface b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_incorrect_iface
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_vpn_incorrect_iface
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_incorrect_iface
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_vpn_one_underlying
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_compression b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_compression
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_vpn_one_underlying_compression
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_compression
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_rewrite_through_self b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_rewrite_through_self
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_vpn_rewrite_through_self
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_rewrite_through_self
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_duplication b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_duplication
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_vpn_two_underlying_duplication
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_duplication
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split_compression b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split_compression
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split_compression
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split_compression
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_with_clat b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_with_clat
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_vpn_with_clat
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_with_clat
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_with_clat
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_after
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_after
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_before
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_before
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_simple b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_simple
similarity index 100%
rename from tests/net/res/raw/xt_qtaguid_with_clat_simple
rename to packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_simple
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 16a946d..f8cb5d3 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -561,7 +561,20 @@
                 break;
         }
 
-        Log.d(TAG, "status=" + statusString + ", cause=" + causeString + ", detail=" + detail);
+        StringBuilder msg = new StringBuilder();
+        msg.append("status: " + statusString + ", cause: " + causeString);
+        if (status == STATUS_IN_PROGRESS) {
+            msg.append(
+                    String.format(
+                            ", partition name: %s, progress: %d/%d",
+                            mCurrentPartitionName,
+                            mCurrentPartitionInstalledSize,
+                            mCurrentPartitionSize));
+        }
+        if (detail != null) {
+            msg.append(", detail: " + detail);
+        }
+        Log.d(TAG, msg.toString());
 
         if (notifyOnNotificationBar) {
             mNM.notify(NOTIFICATION_ID, buildNotification(status, cause, detail));
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index 59ea9f0..f18d426 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -320,20 +320,21 @@
         }
     }
 
-    private void installScratch() throws IOException {
-        final long scratchSize = mDynSystem.suggestScratchSize();
+    private void installWritablePartition(final String partitionName, final long partitionSize)
+            throws IOException {
+        Log.d(TAG, "Creating writable partition: " + partitionName + ", size: " + partitionSize);
+
         Thread thread = new Thread() {
             @Override
             public void run() {
                 mInstallationSession =
-                        mDynSystem.createPartition("scratch", scratchSize, /* readOnly= */ false);
+                        mDynSystem.createPartition(
+                                partitionName, partitionSize, /* readOnly= */ false);
             }
         };
 
-        Log.d(TAG, "Creating partition: scratch, size = " + scratchSize);
         thread.start();
-
-        Progress progress = new Progress("scratch", scratchSize, mNumInstalledPartitions++);
+        Progress progress = new Progress(partitionName, partitionSize, mNumInstalledPartitions++);
 
         while (thread.isAlive()) {
             if (isCancelled()) {
@@ -356,53 +357,22 @@
 
         if (mInstallationSession == null) {
             throw new IOException(
-                    "Failed to start installation with requested size: " + scratchSize);
+                    "Failed to start installation with requested size: " + partitionSize);
         }
+
         // Reset installation session and verify that installation completes successfully.
         mInstallationSession = null;
         if (!mDynSystem.closePartition()) {
-            throw new IOException("Failed to complete partition installation: scratch");
+            throw new IOException("Failed to complete partition installation: " + partitionName);
         }
     }
 
+    private void installScratch() throws IOException {
+        installWritablePartition("scratch", mDynSystem.suggestScratchSize());
+    }
+
     private void installUserdata() throws IOException {
-        Thread thread = new Thread(() -> {
-            mInstallationSession = mDynSystem.createPartition("userdata", mUserdataSize, false);
-        });
-
-        Log.d(TAG, "Creating partition: userdata, size = " + mUserdataSize);
-        thread.start();
-
-        Progress progress = new Progress("userdata", mUserdataSize, mNumInstalledPartitions++);
-
-        while (thread.isAlive()) {
-            if (isCancelled()) {
-                return;
-            }
-
-            final long installedSize = mDynSystem.getInstallationProgress().bytes_processed;
-
-            if (installedSize > progress.installedSize + MIN_PROGRESS_TO_PUBLISH) {
-                progress.installedSize = installedSize;
-                publishProgress(progress);
-            }
-
-            try {
-                Thread.sleep(100);
-            } catch (InterruptedException e) {
-                // Ignore the error.
-            }
-        }
-
-        if (mInstallationSession == null) {
-            throw new IOException(
-                    "Failed to start installation with requested size: " + mUserdataSize);
-        }
-        // Reset installation session and verify that installation completes successfully.
-        mInstallationSession = null;
-        if (!mDynSystem.closePartition()) {
-            throw new IOException("Failed to complete partition installation: userdata");
-        }
+        installWritablePartition("userdata", mUserdataSize);
     }
 
     private void installImages() throws IOException, ImageValidationException {
diff --git a/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm b/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm
index 44d5a0c..1614188 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm
@@ -171,15 +171,15 @@
 }
 
 key LEFT_BRACKET {
-    label:                              '['
+    label:                              ']'
     base, capslock:                     '\u062c'
-    shift:                              '<'
+    shift:                              '>'
 }
 
 key RIGHT_BRACKET {
-    label:                              ']'
+    label:                              '['
     base, capslock:                     '\u062f'
-    shift:                              '>'
+    shift:                              '<'
 }
 
 key BACKSLASH {
diff --git a/packages/InputDevices/res/raw/keyboard_layout_hebrew.kcm b/packages/InputDevices/res/raw/keyboard_layout_hebrew.kcm
index cd3a4b9..283cb4e 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_hebrew.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_hebrew.kcm
@@ -89,14 +89,14 @@
 key 9 {
     label:                              '9'
     base:                               '9'
-    shift:                              '('
+    shift:                              ')'
     shift+capslock:                     '\u05c2'
 }
 
 key 0 {
     label:                              '0'
     base:                               '0'
-    shift:                              ')'
+    shift:                              '('
     shift+capslock:                     '\u05c1'
 }
 
@@ -180,17 +180,17 @@
 }
 
 key LEFT_BRACKET {
-    label:                              '['
-    base, capslock:                     '['
-    shift:                              '{'
-}
-
-key RIGHT_BRACKET {
     label:                              ']'
     base, capslock:                     ']'
     shift:                              '}'
 }
 
+key RIGHT_BRACKET {
+    label:                              '['
+    base, capslock:                     '['
+    shift:                              '{'
+}
+
 ### ROW 3
 
 key A {
@@ -322,14 +322,14 @@
 key COMMA {
     label:                              ','
     base:                               '\u05ea'
-    shift:                              '<'
+    shift:                              '>'
     capslock:                           ','
 }
 
 key PERIOD {
     label:                              '.'
     base:                               '\u05e5'
-    shift:                              '>'
+    shift:                              '<'
     capslock:                           '.'
 }
 
diff --git a/packages/InputDevices/res/raw/keyboard_layout_persian.kcm b/packages/InputDevices/res/raw/keyboard_layout_persian.kcm
index e7dd6c6..bfe7821 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_persian.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_persian.kcm
@@ -292,14 +292,14 @@
 key COMMA {
     label, number:                      '\u0648'
     base:                               '\u0648'
-    shift:                              '<'
+    shift:                              '>'
     ctrl, alt, meta:                    none
 }
 
 key PERIOD {
     label, number:                      '.'
     base:                               '.'
-    shift:                              '>'
+    shift:                              '<'
     ctrl, alt, meta:                    none
 }
 
@@ -440,14 +440,14 @@
 }
 
 key NUMPAD_LEFT_PAREN {
-    label, number:                      '('
-    base:                               '('
+    label, number:                      ')'
+    base:                               ')'
     ctrl, alt, meta:                    none
 }
 
 key NUMPAD_RIGHT_PAREN {
-    label, number:                      ')'
-    base:                               ')'
+    label, number:                      '('
+    base:                               '('
     ctrl, alt, meta:                    none
 }
 
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 27df92f..2a12ce2 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -467,6 +467,9 @@
     <!-- Permission required for CTS test - CtsAlarmManagerTestCases -->
     <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
 
+    <!-- Permission required for CTS test - GlobalSearchSessionPlatformCtsTests -->
+    <uses-permission android:name="android.permission.READ_GLOBAL_APP_SEARCH_DATA" />
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 583953c..5043724 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -80,6 +80,7 @@
     private int mImeHeight;
     private boolean mIsShelfShowing;
     private int mShelfHeight;
+    private boolean mDefaultLandscape;
 
     private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener =
             new DisplayController.OnDisplaysChangedListener() {
@@ -87,6 +88,7 @@
         public void onDisplayAdded(int displayId) {
             if (displayId == mContext.getDisplayId()) {
                 mDisplayLayout.set(mDisplayController.getDisplayLayout(displayId));
+                mDefaultLandscape = (mDisplayInfo.logicalWidth > mDisplayInfo.logicalHeight);
             }
         }
     };
@@ -362,9 +364,17 @@
     private void updateDisplayInfoIfNeeded() {
         final boolean updateNeeded;
         if ((mDisplayInfo.rotation == ROTATION_0) || (mDisplayInfo.rotation == ROTATION_180)) {
-            updateNeeded = (mDisplayInfo.logicalWidth > mDisplayInfo.logicalHeight);
+            if (!mDefaultLandscape) {
+                updateNeeded = (mDisplayInfo.logicalWidth > mDisplayInfo.logicalHeight);
+            } else {
+                updateNeeded = (mDisplayInfo.logicalWidth < mDisplayInfo.logicalHeight);
+            }
         } else {
-            updateNeeded = (mDisplayInfo.logicalWidth < mDisplayInfo.logicalHeight);
+            if (!mDefaultLandscape) {
+                updateNeeded = (mDisplayInfo.logicalWidth < mDisplayInfo.logicalHeight);
+            } else {
+                updateNeeded = (mDisplayInfo.logicalWidth > mDisplayInfo.logicalHeight);
+            }
         }
         if (updateNeeded) {
             final int newLogicalHeight = mDisplayInfo.logicalWidth;
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 0ac8f74..706f738 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -10,7 +10,6 @@
 filegroup {
     name: "services.core-sources",
     srcs: ["java/**/*.java"],
-    exclude_srcs: [":connectivity-service-srcs"],
     path: "java",
     visibility: ["//frameworks/base/services"],
 }
@@ -200,29 +199,3 @@
     src: ":services.core.json.gz",
 }
 
-// TODO: Move connectivity service sources to independent directory.
-filegroup {
-    name: "connectivity-service-srcs",
-    srcs: [
-        "java/com/android/server/ConnectivityService.java",
-        "java/com/android/server/ConnectivityServiceInitializer.java",
-        "java/com/android/server/TestNetworkService.java",
-        "java/com/android/server/connectivity/AutodestructReference.java",
-        "java/com/android/server/connectivity/ConnectivityConstants.java",
-        "java/com/android/server/connectivity/DnsManager.java",
-        "java/com/android/server/connectivity/FullScore.java",
-        "java/com/android/server/connectivity/KeepaliveTracker.java",
-        "java/com/android/server/connectivity/LingerMonitor.java",
-        "java/com/android/server/connectivity/MockableSystemProperties.java",
-        "java/com/android/server/connectivity/Nat464Xlat.java",
-        "java/com/android/server/connectivity/NetworkAgentInfo.java",
-        "java/com/android/server/connectivity/NetworkDiagnostics.java",
-        "java/com/android/server/connectivity/NetworkNotificationManager.java",
-        "java/com/android/server/connectivity/NetworkRanker.java",
-        "java/com/android/server/connectivity/PermissionMonitor.java",
-        "java/com/android/server/connectivity/ProxyTracker.java",
-        "java/com/android/server/connectivity/QosCallbackAgentConnection.java",
-        "java/com/android/server/connectivity/QosCallbackTracker.java",
-        "java/com/android/server/connectivity/TcpKeepaliveController.java",
-    ],
-}
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 9c8582f7..f4a8f37 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -133,6 +133,12 @@
         private static final String SETTINGS_COLLECT_LATENCY_DATA_KEY = "collect_Latency_data";
         private static final String SETTINGS_LATENCY_OBSERVER_SAMPLING_INTERVAL_KEY =
                 "latency_observer_sampling_interval";
+        private static final String SETTINGS_LATENCY_HISTOGRAM_BUCKET_COUNT_KEY =
+                "latency_histogram_bucket_count";
+        private static final String SETTINGS_LATENCY_HISTOGRAM_FIRST_BUCKET_SIZE_KEY =
+                "latency_histogram_first_bucket_size";
+        private static final String SETTINGS_LATENCY_HISTOGRAM_BUCKET_SCALE_FACTOR_KEY =
+                "latency_histogram_bucket_scale_factor";
 
         private boolean mEnabled;
         private final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS);
@@ -190,9 +196,20 @@
                     mParser.getBoolean(SETTINGS_COLLECT_LATENCY_DATA_KEY,
                     BinderCallsStats.DEFAULT_COLLECT_LATENCY_DATA));
             // Binder latency observer settings.
-            mBinderCallsStats.getLatencyObserver().setSamplingInterval(mParser.getInt(
+            BinderLatencyObserver binderLatencyObserver = mBinderCallsStats.getLatencyObserver();
+            binderLatencyObserver.setSamplingInterval(mParser.getInt(
                     SETTINGS_LATENCY_OBSERVER_SAMPLING_INTERVAL_KEY,
                     BinderLatencyObserver.PERIODIC_SAMPLING_INTERVAL_DEFAULT));
+            binderLatencyObserver.setHistogramBucketsParams(
+                    mParser.getInt(
+                        SETTINGS_LATENCY_HISTOGRAM_BUCKET_COUNT_KEY,
+                        BinderLatencyObserver.BUCKET_COUNT_DEFAULT),
+                    mParser.getInt(
+                        SETTINGS_LATENCY_HISTOGRAM_FIRST_BUCKET_SIZE_KEY,
+                        BinderLatencyObserver.FIRST_BUCKET_SIZE_DEFAULT),
+                    mParser.getFloat(
+                        SETTINGS_LATENCY_HISTOGRAM_BUCKET_SCALE_FACTOR_KEY,
+                        BinderLatencyObserver.BUCKET_SCALE_FACTOR_DEFAULT));
 
 
             final boolean enabled =
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index d574e74..d6ee951 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -1112,7 +1112,7 @@
             case IpSecManager.DIRECTION_IN:
                 return;
             case IpSecManager.DIRECTION_FWD:
-                // Only NETWORK_STACK or PERMISSION_NETWORK_STACK allowed to use forward policies
+                // Only NETWORK_STACK or MAINLINE_NETWORK_STACK allowed to use forward policies
                 PermissionUtils.enforceNetworkStackPermission(mContext);
                 return;
         }
@@ -1358,6 +1358,16 @@
                         ikey,
                         0xffffffff,
                         resourceId);
+
+                // Add a forwarding policy on the tunnel interface. In order to support forwarding
+                // the IpSecTunnelInterface must have a forwarding policy matching the incoming SA.
+                //
+                // Unless a IpSecTransform is also applied against this interface in DIRECTION_FWD,
+                // forwarding will be blocked by default (as would be the case if this policy was
+                // absent).
+                //
+                // This is necessary only on the tunnel interface, and not any the interface to
+                // which traffic will be forwarded to.
                 netd.ipSecAddSecurityPolicy(
                         callerUid,
                         selAddrFamily,
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 1fd637a..bcbd692 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -167,7 +167,6 @@
     @NonNull private final VcnNetworkProvider mNetworkProvider;
     @NonNull private final TelephonySubscriptionTrackerCallback mTelephonySubscriptionTrackerCb;
     @NonNull private final TelephonySubscriptionTracker mTelephonySubscriptionTracker;
-    @NonNull private final VcnContext mVcnContext;
     @NonNull private final BroadcastReceiver mPkgChangeReceiver;
 
     @NonNull
@@ -212,7 +211,6 @@
                 mContext, mLooper, mTelephonySubscriptionTrackerCb);
 
         mConfigDiskRwHelper = mDeps.newPersistableBundleLockingReadWriteHelper(VCN_CONFIG_FILE);
-        mVcnContext = mDeps.newVcnContext(mContext, mLooper, mNetworkProvider);
 
         mPkgChangeReceiver = new BroadcastReceiver() {
             @Override
@@ -336,8 +334,9 @@
         public VcnContext newVcnContext(
                 @NonNull Context context,
                 @NonNull Looper looper,
-                @NonNull VcnNetworkProvider vcnNetworkProvider) {
-            return new VcnContext(context, looper, vcnNetworkProvider);
+                @NonNull VcnNetworkProvider vcnNetworkProvider,
+                boolean isInTestMode) {
+            return new VcnContext(context, looper, vcnNetworkProvider, isInTestMode);
         }
 
         /** Creates a new Vcn instance using the provided configuration */
@@ -421,6 +420,14 @@
                 "Carrier privilege required for subscription group to set VCN Config");
     }
 
+    private void enforceManageTestNetworksForTestMode(@NonNull VcnConfig vcnConfig) {
+        if (vcnConfig.isTestModeProfile()) {
+            mContext.enforceCallingPermission(
+                    android.Manifest.permission.MANAGE_TEST_NETWORKS,
+                    "Test-mode require the MANAGE_TEST_NETWORKS permission");
+        }
+    }
+
     private class VcnSubscriptionTrackerCallback implements TelephonySubscriptionTrackerCallback {
         /**
          * Handles subscription group changes, as notified by {@link TelephonySubscriptionTracker}
@@ -544,8 +551,11 @@
 
         final VcnCallbackImpl vcnCallback = new VcnCallbackImpl(subscriptionGroup);
 
+        final VcnContext vcnContext =
+                mDeps.newVcnContext(
+                        mContext, mLooper, mNetworkProvider, config.isTestModeProfile());
         final Vcn newInstance =
-                mDeps.newVcn(mVcnContext, subscriptionGroup, config, mLastSnapshot, vcnCallback);
+                mDeps.newVcn(vcnContext, subscriptionGroup, config, mLastSnapshot, vcnCallback);
         mVcns.put(subscriptionGroup, newInstance);
 
         // Now that a new VCN has started, notify all registered listeners to refresh their
@@ -589,6 +599,7 @@
 
         mContext.getSystemService(AppOpsManager.class)
                 .checkPackage(mDeps.getBinderCallingUid(), config.getProvisioningPackageName());
+        enforceManageTestNetworksForTestMode(config);
         enforceCallingUserAndCarrierPrivilege(subscriptionGroup, opPkgName);
 
         Binder.withCleanCallingIdentity(() -> {
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index a37115d..2e38278 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -96,6 +96,7 @@
         "/system/bin/audioserver",
         "/system/bin/cameraserver",
         "/system/bin/drmserver",
+        "/system/bin/keystore2",
         "/system/bin/mediadrmserver",
         "/system/bin/mediaserver",
         "/system/bin/netd",
diff --git a/services/core/java/com/android/server/am/LmkdConnection.java b/services/core/java/com/android/server/am/LmkdConnection.java
index f41c364..1ecb9eb 100644
--- a/services/core/java/com/android/server/am/LmkdConnection.java
+++ b/services/core/java/com/android/server/am/LmkdConnection.java
@@ -31,6 +31,8 @@
 
 import libcore.io.IoUtils;
 
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.InputStream;
@@ -43,8 +45,12 @@
 public class LmkdConnection {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "LmkdConnection" : TAG_AM;
 
-    // lmkd reply max size in bytes
-    private static final int LMKD_REPLY_MAX_SIZE = 12;
+    /**
+     * Max LMKD reply packet length in bytes
+     * Used to hold the data for the statsd atoms logging
+     * Must be in sync with statslog.h
+     */
+    private static final int LMKD_REPLY_MAX_SIZE = 214;
 
     // connection listener interface
     interface LmkdConnectionListener {
@@ -70,7 +76,7 @@
          * @param receivedLen Size of the data received
          * @return True if the message has been handled correctly, false otherwise.
          */
-        boolean handleUnsolicitedMessage(ByteBuffer dataReceived, int receivedLen);
+        boolean handleUnsolicitedMessage(DataInputStream inputData, int receivedLen);
     }
 
     private final MessageQueue mMsgQueue;
@@ -95,6 +101,10 @@
     private final ByteBuffer mInputBuf =
             ByteBuffer.allocate(LMKD_REPLY_MAX_SIZE);
 
+    // Input stream to parse the incoming data
+    private final DataInputStream mInputData = new DataInputStream(
+            new ByteArrayInputStream(mInputBuf.array()));
+
     // object to protect mReplyBuf and to wait/notify when reply is received
     private final Object mReplyBufLock = new Object();
 
@@ -186,26 +196,32 @@
     private void processIncomingData() {
         int len = read(mInputBuf);
         if (len > 0) {
-            synchronized (mReplyBufLock) {
-                if (mReplyBuf != null) {
-                    if (mListener.isReplyExpected(mReplyBuf, mInputBuf, len)) {
-                        // copy into reply buffer
-                        mReplyBuf.put(mInputBuf.array(), 0, len);
-                        mReplyBuf.rewind();
-                        // wakeup the waiting thread
-                        mReplyBufLock.notifyAll();
-                    } else if (!mListener.handleUnsolicitedMessage(mInputBuf, len)) {
-                        // received unexpected packet
-                        // treat this as an error
-                        mReplyBuf = null;
-                        mReplyBufLock.notifyAll();
-                        Slog.e(TAG, "Received an unexpected packet from lmkd");
+            try {
+                // reset InputStream to point into mInputBuf.array() begin
+                mInputData.reset();
+                synchronized (mReplyBufLock) {
+                    if (mReplyBuf != null) {
+                        if (mListener.isReplyExpected(mReplyBuf, mInputBuf, len)) {
+                            // copy into reply buffer
+                            mReplyBuf.put(mInputBuf.array(), 0, len);
+                            mReplyBuf.rewind();
+                            // wakeup the waiting thread
+                            mReplyBufLock.notifyAll();
+                        } else if (!mListener.handleUnsolicitedMessage(mInputData, len)) {
+                            // received unexpected packet
+                            // treat this as an error
+                            mReplyBuf = null;
+                            mReplyBufLock.notifyAll();
+                            Slog.e(TAG, "Received an unexpected packet from lmkd");
+                        }
+                    } else if (!mListener.handleUnsolicitedMessage(mInputData, len)) {
+                        // received asynchronous communication from lmkd
+                        // but we don't recognize it.
+                        Slog.w(TAG, "Received an unexpected packet from lmkd");
                     }
-                } else if (!mListener.handleUnsolicitedMessage(mInputBuf, len)) {
-                    // received asynchronous communication from lmkd
-                    // but we don't recognize it.
-                    Slog.w(TAG, "Received an unexpected packet from lmkd");
                 }
+            } catch (IOException e) {
+                Slog.e(TAG, "Failed to parse lmkd data buffer. Size = " + len);
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/LmkdStatsReporter.java b/services/core/java/com/android/server/am/LmkdStatsReporter.java
new file mode 100644
index 0000000..c702d78
--- /dev/null
+++ b/services/core/java/com/android/server/am/LmkdStatsReporter.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2021 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 com.android.server.am;
+
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.util.Slog;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * Activity manager communication with lmkd data handling and statsd atom logging
+ */
+public final class LmkdStatsReporter {
+
+    static final String TAG = TAG_WITH_CLASS_NAME ? "LmkdStatsReporter" : TAG_AM;
+
+    public static final int KILL_OCCURRED_MSG_SIZE = 80;
+    public static final int STATE_CHANGED_MSG_SIZE = 8;
+
+    private static final int PRESSURE_AFTER_KILL = 0;
+    private static final int NOT_RESPONDING = 1;
+    private static final int LOW_SWAP_AND_THRASHING = 2;
+    private static final int LOW_MEM_AND_SWAP = 3;
+    private static final int LOW_MEM_AND_THRASHING = 4;
+    private static final int DIRECT_RECL_AND_THRASHING = 5;
+    private static final int LOW_MEM_AND_SWAP_UTIL = 6;
+
+    /**
+     * Processes the LMK_KILL_OCCURRED packet data
+     * Logs the event when LMKD kills a process to reduce memory pressure.
+     * Code: LMK_KILL_OCCURRED = 51
+     */
+    public static void logKillOccurred(DataInputStream inputData) {
+        try {
+            final long pgFault = inputData.readLong();
+            final long pgMajFault = inputData.readLong();
+            final long rssInBytes = inputData.readLong();
+            final long cacheInBytes = inputData.readLong();
+            final long swapInBytes = inputData.readLong();
+            final long processStartTimeNS = inputData.readLong();
+            final int uid = inputData.readInt();
+            final int oomScore = inputData.readInt();
+            final int minOomScore = inputData.readInt();
+            final int freeMemKb = inputData.readInt();
+            final int freeSwapKb = inputData.readInt();
+            final int killReason = inputData.readInt();
+            final String procName = inputData.readUTF();
+
+            FrameworkStatsLog.write(FrameworkStatsLog.LMK_KILL_OCCURRED, uid, procName, oomScore,
+                    pgFault, pgMajFault, rssInBytes, cacheInBytes, swapInBytes, processStartTimeNS,
+                    minOomScore, freeMemKb, freeSwapKb, mapKillReason(killReason));
+        } catch (IOException e) {
+            Slog.e(TAG, "Invalid buffer data. Failed to log LMK_KILL_OCCURRED");
+            return;
+        }
+    }
+
+    /**
+     * Processes the LMK_STATE_CHANGED packet
+     * Logs the change in LMKD state which is used as start/stop boundaries for logging
+     * LMK_KILL_OCCURRED event.
+     * Code: LMK_STATE_CHANGED = 54
+     */
+    public static void logStateChanged(int state) {
+        FrameworkStatsLog.write(FrameworkStatsLog.LMK_STATE_CHANGED, state);
+    }
+
+    private static int mapKillReason(int reason) {
+        switch (reason) {
+            case PRESSURE_AFTER_KILL:
+                return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__PRESSURE_AFTER_KILL;
+            case NOT_RESPONDING:
+                return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__NOT_RESPONDING;
+            case LOW_SWAP_AND_THRASHING:
+                return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__LOW_SWAP_AND_THRASHING;
+            case LOW_MEM_AND_SWAP:
+                return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__LOW_MEM_AND_SWAP;
+            case LOW_MEM_AND_THRASHING:
+                return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__LOW_MEM_AND_THRASHING;
+            case DIRECT_RECL_AND_THRASHING:
+                return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__DIRECT_RECL_AND_THRASHING;
+            case LOW_MEM_AND_SWAP_UTIL:
+                return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__LOW_MEM_AND_SWAP_UTIL;
+            default:
+                return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__UNKNOWN;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 444418c..e9b0146 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -125,6 +125,7 @@
 
 import dalvik.system.VMRuntime;
 
+import java.io.DataInputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -316,18 +317,25 @@
     // LMK_GETKILLCNT
     // LMK_SUBSCRIBE
     // LMK_PROCKILL
+    // LMK_UPDATE_PROPS
+    // LMK_KILL_OCCURRED
+    // LMK_STATE_CHANGED
     static final byte LMK_TARGET = 0;
     static final byte LMK_PROCPRIO = 1;
     static final byte LMK_PROCREMOVE = 2;
     static final byte LMK_PROCPURGE = 3;
     static final byte LMK_GETKILLCNT = 4;
     static final byte LMK_SUBSCRIBE = 5;
-    static final byte LMK_PROCKILL = 6; // Note: this is an unsolicated command
+    static final byte LMK_PROCKILL = 6; // Note: this is an unsolicited command
+    static final byte LMK_UPDATE_PROPS = 7;
+    static final byte LMK_KILL_OCCURRED = 8; // Msg to subscribed clients on kill occurred event
+    static final byte LMK_STATE_CHANGED = 9; // Msg to subscribed clients on state changed
 
     // Low Memory Killer Daemon command codes.
     // These must be kept in sync with async_event_type definitions in lmkd.h
     //
     static final int LMK_ASYNC_EVENT_KILL = 0;
+    static final int LMK_ASYNC_EVENT_STAT = 1;
 
     // lmkd reconnect delay in msecs
     private static final long LMKD_RECONNECT_DELAY_MS = 1000;
@@ -776,22 +784,44 @@
                         }
 
                         @Override
-                        public boolean handleUnsolicitedMessage(ByteBuffer dataReceived,
+                        public boolean handleUnsolicitedMessage(DataInputStream inputData,
                                 int receivedLen) {
                             if (receivedLen < 4) {
                                 return false;
                             }
-                            switch (dataReceived.getInt(0)) {
-                                case LMK_PROCKILL:
-                                    if (receivedLen != 12) {
+
+                            try {
+                                switch (inputData.readInt()) {
+                                    case LMK_PROCKILL:
+                                        if (receivedLen != 12) {
+                                            return false;
+                                        }
+                                        final int pid = inputData.readInt();
+                                        final int uid = inputData.readInt();
+                                        mAppExitInfoTracker.scheduleNoteLmkdProcKilled(pid, uid);
+                                        return true;
+                                    case LMK_KILL_OCCURRED:
+                                        if (receivedLen
+                                                < LmkdStatsReporter.KILL_OCCURRED_MSG_SIZE) {
+                                            return false;
+                                        }
+                                        LmkdStatsReporter.logKillOccurred(inputData);
+                                        return true;
+                                    case LMK_STATE_CHANGED:
+                                        if (receivedLen
+                                                != LmkdStatsReporter.STATE_CHANGED_MSG_SIZE) {
+                                            return false;
+                                        }
+                                        final int state = inputData.readInt();
+                                        LmkdStatsReporter.logStateChanged(state);
+                                        return true;
+                                    default:
                                         return false;
-                                    }
-                                    mAppExitInfoTracker.scheduleNoteLmkdProcKilled(
-                                            dataReceived.getInt(4), dataReceived.getInt(8));
-                                    return true;
-                                default:
-                                    return false;
+                                }
+                            } catch (IOException e) {
+                                Slog.e(TAG, "Invalid buffer data. Failed to log LMK_KILL_OCCURRED");
                             }
+                            return false;
                         }
                     }
             );
@@ -1433,6 +1463,12 @@
             buf.putInt(LMK_SUBSCRIBE);
             buf.putInt(LMK_ASYNC_EVENT_KILL);
             ostream.write(buf.array(), 0, buf.position());
+
+            // Subscribe for stats event notifications
+            buf = ByteBuffer.allocate(4 * 2);
+            buf.putInt(LMK_SUBSCRIBE);
+            buf.putInt(LMK_ASYNC_EVENT_STAT);
+            ostream.write(buf.array(), 0, buf.position());
         } catch (IOException ex) {
             return false;
         }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 22a6044..d0a3079 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -5860,6 +5860,10 @@
                     if (index == -1) {
                         continue;
                     }
+                    if (mPublicStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED
+                            && mCameraSoundForced) {
+                        index = mIndexMax;
+                    }
                     if (DEBUG_VOL) {
                         Log.v(TAG, "readSettings: found stored index " + getValidIndex(index)
                                  + " for group " + mAudioVolumeGroup.name() + ", device: " + name);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
index 740407c..e233084 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
@@ -86,6 +86,8 @@
         pw.println("      Send a Vendor Command to the given target device");
         pw.println("  setsystemaudiomode, setsam [on|off]");
         pw.println("      Sets the System Audio Mode feature on or off on TV devices");
+        pw.println("  setarc [on|off]");
+        pw.println("      Sets the ARC feature on or off on TV devices");
     }
 
     private int handleShellCommand(String cmd) throws RemoteException {
@@ -100,6 +102,8 @@
             case "setsystemaudiomode":
             case "setsam":
                 return setSystemAudioMode(pw);
+            case "setarc":
+                return setArcMode(pw);
         }
 
         getErrPrintWriter().println("Unhandled command: " + cmd);
@@ -188,6 +192,27 @@
         return mCecResult.get() == HdmiControlManager.RESULT_SUCCESS ? 0 : 1;
     }
 
+    private int setArcMode(PrintWriter pw) throws RemoteException {
+        if (1 > getRemainingArgsCount()) {
+            throw new IllegalArgumentException(
+                    "Please indicate if ARC mode should be turned \"on\" or \"off\".");
+        }
+
+        String arg = getNextArg();
+        if (arg.equals("on")) {
+            pw.println("Setting ARC mode on");
+            mBinderService.setArcMode(true);
+        } else if (arg.equals("off")) {
+            pw.println("Setting ARC mode off");
+            mBinderService.setArcMode(false);
+        } else {
+            throw new IllegalArgumentException(
+                    "Please indicate if ARC mode should be turned \"on\" or \"off\".");
+        }
+
+        return 0;
+    }
+
     private boolean receiveCallback(String command) {
         try {
             if (!mLatch.await(HdmiConfig.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index e7c0a50..431b009 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -382,8 +382,8 @@
 
         // Migrate data usage over a VPN to the TUN network.
         for (UnderlyingNetworkInfo info : vpnArray) {
-            delta.migrateTun(info.getOwnerUid(), info.getIface(),
-                    info.getUnderlyingIfaces());
+            delta.migrateTun(info.getOwnerUid(), info.getInterface(),
+                    info.getUnderlyingInterfaces());
             // Filter out debug entries as that may lead to over counting.
             delta.filterDebugEntries();
         }
diff --git a/services/core/java/com/android/server/os/OWNERS b/services/core/java/com/android/server/os/OWNERS
new file mode 100644
index 0000000..1957332
--- /dev/null
+++ b/services/core/java/com/android/server/os/OWNERS
@@ -0,0 +1,2 @@
+# Bugreporting
+per-file Bugreport* = file:/platform/frameworks/native:/cmds/dumpstate/OWNERS
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index fa64df5..836e615 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5257,6 +5257,7 @@
         long[] pattern;
         switch (effectId) {
             case HapticFeedbackConstants.CONTEXT_CLICK:
+            case HapticFeedbackConstants.GESTURE_END:
                 return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
             case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
                 if (!mHapticTextHandleEnabled) {
@@ -5269,7 +5270,6 @@
             case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
             case HapticFeedbackConstants.ENTRY_BUMP:
             case HapticFeedbackConstants.DRAG_CROSSING:
-            case HapticFeedbackConstants.GESTURE_END:
                 return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
             case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS
             case HapticFeedbackConstants.VIRTUAL_KEY:
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index 19fbdbd..5565ccb 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -145,7 +145,7 @@
      */
     public void handleSubscriptionsChanged() {
         final Map<ParcelUuid, Set<String>> privilegedPackages = new HashMap<>();
-        final Map<Integer, ParcelUuid> newSubIdToGroupMap = new HashMap<>();
+        final Map<Integer, SubscriptionInfo> newSubIdToInfoMap = new HashMap<>();
 
         final List<SubscriptionInfo> allSubs = mSubscriptionManager.getAllSubscriptionInfoList();
         if (allSubs == null) {
@@ -160,7 +160,7 @@
             }
 
             // Build subId -> subGrp cache
-            newSubIdToGroupMap.put(subInfo.getSubscriptionId(), subInfo.getGroupUuid());
+            newSubIdToInfoMap.put(subInfo.getSubscriptionId(), subInfo);
 
             // Update subscription groups that are both ready, and active. For a group to be
             // considered active, both of the following must be true:
@@ -186,7 +186,7 @@
         }
 
         final TelephonySubscriptionSnapshot newSnapshot =
-                new TelephonySubscriptionSnapshot(newSubIdToGroupMap, privilegedPackages);
+                new TelephonySubscriptionSnapshot(newSubIdToInfoMap, privilegedPackages);
 
         // If snapshot was meaningfully updated, fire the callback
         if (!newSnapshot.equals(mCurrentSnapshot)) {
@@ -245,7 +245,7 @@
 
     /** TelephonySubscriptionSnapshot is a class containing info about active subscriptions */
     public static class TelephonySubscriptionSnapshot {
-        private final Map<Integer, ParcelUuid> mSubIdToGroupMap;
+        private final Map<Integer, SubscriptionInfo> mSubIdToInfoMap;
         private final Map<ParcelUuid, Set<String>> mPrivilegedPackages;
 
         public static final TelephonySubscriptionSnapshot EMPTY_SNAPSHOT =
@@ -253,12 +253,12 @@
 
         @VisibleForTesting(visibility = Visibility.PRIVATE)
         TelephonySubscriptionSnapshot(
-                @NonNull Map<Integer, ParcelUuid> subIdToGroupMap,
+                @NonNull Map<Integer, SubscriptionInfo> subIdToInfoMap,
                 @NonNull Map<ParcelUuid, Set<String>> privilegedPackages) {
-            Objects.requireNonNull(subIdToGroupMap, "subIdToGroupMap was null");
+            Objects.requireNonNull(subIdToInfoMap, "subIdToInfoMap was null");
             Objects.requireNonNull(privilegedPackages, "privilegedPackages was null");
 
-            mSubIdToGroupMap = Collections.unmodifiableMap(subIdToGroupMap);
+            mSubIdToInfoMap = Collections.unmodifiableMap(subIdToInfoMap);
 
             final Map<ParcelUuid, Set<String>> unmodifiableInnerSets = new ArrayMap<>();
             for (Entry<ParcelUuid, Set<String>> entry : privilegedPackages.entrySet()) {
@@ -285,7 +285,9 @@
         /** Returns the Subscription Group for a given subId. */
         @Nullable
         public ParcelUuid getGroupForSubId(int subId) {
-            return mSubIdToGroupMap.get(subId);
+            return mSubIdToInfoMap.containsKey(subId)
+                    ? mSubIdToInfoMap.get(subId).getGroupUuid()
+                    : null;
         }
 
         /**
@@ -295,8 +297,8 @@
         public Set<Integer> getAllSubIdsInGroup(ParcelUuid subGrp) {
             final Set<Integer> subIds = new ArraySet<>();
 
-            for (Entry<Integer, ParcelUuid> entry : mSubIdToGroupMap.entrySet()) {
-                if (subGrp.equals(entry.getValue())) {
+            for (Entry<Integer, SubscriptionInfo> entry : mSubIdToInfoMap.entrySet()) {
+                if (subGrp.equals(entry.getValue().getGroupUuid())) {
                     subIds.add(entry.getKey());
                 }
             }
@@ -304,9 +306,17 @@
             return subIds;
         }
 
+        /** Checks if the requested subscription is opportunistic */
+        @NonNull
+        public boolean isOpportunistic(int subId) {
+            return mSubIdToInfoMap.containsKey(subId)
+                    ? mSubIdToInfoMap.get(subId).isOpportunistic()
+                    : false;
+        }
+
         @Override
         public int hashCode() {
-            return Objects.hash(mSubIdToGroupMap, mPrivilegedPackages);
+            return Objects.hash(mSubIdToInfoMap, mPrivilegedPackages);
         }
 
         @Override
@@ -317,7 +327,7 @@
 
             final TelephonySubscriptionSnapshot other = (TelephonySubscriptionSnapshot) obj;
 
-            return mSubIdToGroupMap.equals(other.mSubIdToGroupMap)
+            return mSubIdToInfoMap.equals(other.mSubIdToInfoMap)
                     && mPrivilegedPackages.equals(other.mPrivilegedPackages);
         }
 
@@ -326,7 +336,7 @@
             pw.println("TelephonySubscriptionSnapshot:");
             pw.increaseIndent();
 
-            pw.println("mSubIdToGroupMap: " + mSubIdToGroupMap);
+            pw.println("mSubIdToInfoMap: " + mSubIdToInfoMap);
             pw.println("mPrivilegedPackages: " + mPrivilegedPackages);
 
             pw.decreaseIndent();
@@ -335,7 +345,7 @@
         @Override
         public String toString() {
             return "TelephonySubscriptionSnapshot{ "
-                    + "mSubIdToGroupMap=" + mSubIdToGroupMap
+                    + "mSubIdToInfoMap=" + mSubIdToInfoMap
                     + ", mPrivilegedPackages=" + mPrivilegedPackages
                     + " }";
         }
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
index 8818023..fb4c623 100644
--- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
+++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
@@ -16,6 +16,10 @@
 
 package com.android.server.vcn;
 
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.ConnectivityManager;
@@ -25,8 +29,16 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
 import android.net.TelephonyNetworkSpecifier;
+import android.net.vcn.VcnManager;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.ParcelUuid;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -35,9 +47,13 @@
 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.TreeSet;
 
 /**
  * Tracks a set of Networks underpinning a VcnGatewayConnection.
@@ -51,6 +67,45 @@
 public class UnderlyingNetworkTracker {
     @NonNull private static final String TAG = UnderlyingNetworkTracker.class.getSimpleName();
 
+    /**
+     * Minimum signal strength for a WiFi network to be eligible for switching to
+     *
+     * <p>A network that satisfies this is eligible to become the selected underlying network with
+     * no additional conditions
+     */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    static final int WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT = -70;
+
+    /**
+     * Minimum signal strength to continue using a WiFi network
+     *
+     * <p>A network that satisfies the conditions may ONLY continue to be used if it is already
+     * selected as the underlying network. A WiFi network satisfying this condition, but NOT the
+     * prospective-network RSSI threshold CANNOT be switched to.
+     */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    static final int WIFI_EXIT_RSSI_THRESHOLD_DEFAULT = -74;
+
+    /** Priority for any cellular network for which the subscription is listed as opportunistic */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    static final int PRIORITY_OPPORTUNISTIC_CELLULAR = 0;
+
+    /** Priority for any WiFi network which is in use, and satisfies the in-use RSSI threshold */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    static final int PRIORITY_WIFI_IN_USE = 1;
+
+    /** Priority for any WiFi network which satisfies the prospective-network RSSI threshold */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    static final int PRIORITY_WIFI_PROSPECTIVE = 2;
+
+    /** Priority for any standard macro cellular network */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    static final int PRIORITY_MACRO_CELLULAR = 3;
+
+    /** Priority for any other networks (including unvalidated, etc) */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    static final int PRIORITY_ANY = Integer.MAX_VALUE;
+
     @NonNull private final VcnContext mVcnContext;
     @NonNull private final ParcelUuid mSubscriptionGroup;
     @NonNull private final Set<Integer> mRequiredUnderlyingNetworkCapabilities;
@@ -58,12 +113,17 @@
     @NonNull private final Dependencies mDeps;
     @NonNull private final Handler mHandler;
     @NonNull private final ConnectivityManager mConnectivityManager;
+    @NonNull private final TelephonyCallback mActiveDataSubIdListener =
+            new VcnActiveDataSubscriptionIdListener();
 
     @NonNull private final List<NetworkCallback> mCellBringupCallbacks = new ArrayList<>();
     @Nullable private NetworkCallback mWifiBringupCallback;
-    @Nullable private NetworkCallback mRouteSelectionCallback;
+    @Nullable private NetworkCallback mWifiEntryRssiThresholdCallback;
+    @Nullable private NetworkCallback mWifiExitRssiThresholdCallback;
+    @Nullable private UnderlyingNetworkListener mRouteSelectionCallback;
 
     @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
+    @Nullable private PersistableBundle mCarrierConfig;
     private boolean mIsQuitting = false;
 
     @Nullable private UnderlyingNetworkRecord mCurrentRecord;
@@ -104,6 +164,30 @@
         mHandler = new Handler(mVcnContext.getLooper());
 
         mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class);
+        mVcnContext
+                .getContext()
+                .getSystemService(TelephonyManager.class)
+                .registerTelephonyCallback(new HandlerExecutor(mHandler), mActiveDataSubIdListener);
+
+        // TODO: Listen for changes in carrier config that affect this.
+        for (int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) {
+            PersistableBundle config =
+                    mVcnContext
+                            .getContext()
+                            .getSystemService(CarrierConfigManager.class)
+                            .getConfigForSubId(subId);
+
+            if (config != null) {
+                mCarrierConfig = config;
+
+                // Attempt to use (any) non-opportunistic subscription. If this subscription is
+                // opportunistic, continue and try to find a non-opportunistic subscription, using
+                // the opportunistic ones as a last resort.
+                if (!isOpportunistic(mLastSnapshot, Collections.singleton(subId))) {
+                    break;
+                }
+            }
+        }
 
         registerOrUpdateNetworkRequests();
     }
@@ -111,16 +195,30 @@
     private void registerOrUpdateNetworkRequests() {
         NetworkCallback oldRouteSelectionCallback = mRouteSelectionCallback;
         NetworkCallback oldWifiCallback = mWifiBringupCallback;
+        NetworkCallback oldWifiEntryRssiThresholdCallback = mWifiEntryRssiThresholdCallback;
+        NetworkCallback oldWifiExitRssiThresholdCallback = mWifiExitRssiThresholdCallback;
         List<NetworkCallback> oldCellCallbacks = new ArrayList<>(mCellBringupCallbacks);
         mCellBringupCallbacks.clear();
 
         // Register new callbacks. Make-before-break; always register new callbacks before removal
         // of old callbacks
         if (!mIsQuitting) {
-            mRouteSelectionCallback = new RouteSelectionCallback();
-            mConnectivityManager.requestBackgroundNetwork(
+            mRouteSelectionCallback = new UnderlyingNetworkListener();
+            mConnectivityManager.registerNetworkCallback(
                     getRouteSelectionRequest(), mRouteSelectionCallback, mHandler);
 
+            mWifiEntryRssiThresholdCallback = new NetworkBringupCallback();
+            mConnectivityManager.registerNetworkCallback(
+                    getWifiEntryRssiThresholdNetworkRequest(),
+                    mWifiEntryRssiThresholdCallback,
+                    mHandler);
+
+            mWifiExitRssiThresholdCallback = new NetworkBringupCallback();
+            mConnectivityManager.registerNetworkCallback(
+                    getWifiExitRssiThresholdNetworkRequest(),
+                    mWifiExitRssiThresholdCallback,
+                    mHandler);
+
             mWifiBringupCallback = new NetworkBringupCallback();
             mConnectivityManager.requestBackgroundNetwork(
                     getWifiNetworkRequest(), mWifiBringupCallback, mHandler);
@@ -135,6 +233,8 @@
         } else {
             mRouteSelectionCallback = null;
             mWifiBringupCallback = null;
+            mWifiEntryRssiThresholdCallback = null;
+            mWifiExitRssiThresholdCallback = null;
             // mCellBringupCallbacks already cleared above.
         }
 
@@ -145,6 +245,12 @@
         if (oldWifiCallback != null) {
             mConnectivityManager.unregisterNetworkCallback(oldWifiCallback);
         }
+        if (oldWifiEntryRssiThresholdCallback != null) {
+            mConnectivityManager.unregisterNetworkCallback(oldWifiEntryRssiThresholdCallback);
+        }
+        if (oldWifiExitRssiThresholdCallback != null) {
+            mConnectivityManager.unregisterNetworkCallback(oldWifiExitRssiThresholdCallback);
+        }
         for (NetworkCallback cellBringupCallback : oldCellCallbacks) {
             mConnectivityManager.unregisterNetworkCallback(cellBringupCallback);
         }
@@ -158,9 +264,18 @@
      * carrier owned networks may be selected, as the request specifies only subIds in the VCN's
      * subscription group, while the VCN networks are excluded by virtue of not having subIds set on
      * the VCN-exposed networks.
+     *
+     * <p>If the VCN that this UnderlyingNetworkTracker belongs to is in test-mode, this will return
+     * a NetworkRequest that only matches Test Networks.
      */
     private NetworkRequest getRouteSelectionRequest() {
+        if (mVcnContext.isInTestMode()) {
+            return getTestNetworkRequest(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup));
+        }
+
         return getBaseNetworkRequestBuilder()
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
                 .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
                 .build();
     }
@@ -182,6 +297,38 @@
     }
 
     /**
+     * Builds the WiFi entry threshold signal strength request
+     *
+     * <p>This request ensures that WiFi reports the crossing of the wifi entry RSSI threshold.
+     * Without this request, WiFi rate-limits, and reports signal strength changes at too slow a
+     * pace to effectively select a short-lived WiFi offload network.
+     */
+    private NetworkRequest getWifiEntryRssiThresholdNetworkRequest() {
+        return getBaseNetworkRequestBuilder()
+                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
+                // Ensure wifi updates signal strengths when crossing this threshold.
+                .setSignalStrength(getWifiEntryRssiThreshold(mCarrierConfig))
+                .build();
+    }
+
+    /**
+     * Builds the WiFi exit threshold signal strength request
+     *
+     * <p>This request ensures that WiFi reports the crossing of the wifi exit RSSI threshold.
+     * Without this request, WiFi rate-limits, and reports signal strength changes at too slow a
+     * pace to effectively select away from a failing WiFi network.
+     */
+    private NetworkRequest getWifiExitRssiThresholdNetworkRequest() {
+        return getBaseNetworkRequestBuilder()
+                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
+                // Ensure wifi updates signal strengths when crossing this threshold.
+                .setSignalStrength(getWifiExitRssiThreshold(mCarrierConfig))
+                .build();
+    }
+
+    /**
      * Builds a Cellular bringup request for a given subId
      *
      * <p>This request is filed in order to ensure that the Telephony stack always has a
@@ -210,6 +357,15 @@
                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
     }
 
+    /** Builds and returns a NetworkRequest for the given subIds to match Test Networks. */
+    private NetworkRequest getTestNetworkRequest(@NonNull Set<Integer> subIds) {
+        return new NetworkRequest.Builder()
+                .clearCapabilities()
+                .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+                .setSubscriptionIds(subIds)
+                .build();
+    }
+
     /**
      * Update this UnderlyingNetworkTracker's TelephonySubscriptionSnapshot.
      *
@@ -217,10 +373,18 @@
      * reevaluate its NetworkBringupCallbacks. This may result in NetworkRequests being registered
      * or unregistered if the subIds mapped to the this Tracker's SubscriptionGroup change.
      */
-    public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
-        Objects.requireNonNull(snapshot, "Missing snapshot");
+    public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot newSnapshot) {
+        Objects.requireNonNull(newSnapshot, "Missing newSnapshot");
 
-        mLastSnapshot = snapshot;
+        final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot;
+        mLastSnapshot = newSnapshot;
+
+        // Only trigger re-registration if subIds in this group have changed
+        if (oldSnapshot
+                .getAllSubIdsInGroup(mSubscriptionGroup)
+                .equals(newSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))) {
+            return;
+        }
         registerOrUpdateNetworkRequests();
     }
 
@@ -231,88 +395,43 @@
 
         // Will unregister all existing callbacks, but not register new ones due to quitting flag.
         registerOrUpdateNetworkRequests();
+
+        mVcnContext
+                .getContext()
+                .getSystemService(TelephonyManager.class)
+                .unregisterTelephonyCallback(mActiveDataSubIdListener);
     }
 
-    /** Returns whether the currently selected Network matches the given network. */
-    private static boolean isSameNetwork(
-            @Nullable UnderlyingNetworkRecord.Builder recordInProgress, @NonNull Network network) {
-        return recordInProgress != null && recordInProgress.getNetwork().equals(network);
-    }
+    private void reevaluateNetworks() {
+        TreeSet<UnderlyingNetworkRecord> sorted =
+                new TreeSet<>(
+                        UnderlyingNetworkRecord.getComparator(
+                                mSubscriptionGroup, mLastSnapshot, mCurrentRecord, mCarrierConfig));
+        sorted.addAll(mRouteSelectionCallback.getUnderlyingNetworks());
 
-    /** Notify the Callback if a full UnderlyingNetworkRecord exists. */
-    private void maybeNotifyCallback() {
-        // Only forward this update if a complete record has been received
-        if (!mRecordInProgress.isValid()) {
+        UnderlyingNetworkRecord candidate = sorted.isEmpty() ? null : sorted.first();
+        if (Objects.equals(mCurrentRecord, candidate)) {
             return;
         }
 
-        // Only forward this update if the updated record differs form the current record
-        UnderlyingNetworkRecord updatedRecord = mRecordInProgress.build();
-        if (!updatedRecord.equals(mCurrentRecord)) {
-            mCurrentRecord = updatedRecord;
-
-            mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord);
-        }
+        mCurrentRecord = candidate;
+        mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord);
     }
 
-    private void handleNetworkAvailable(@NonNull Network network) {
-        mVcnContext.ensureRunningOnLooperThread();
-
-        mRecordInProgress = new UnderlyingNetworkRecord.Builder(network);
-    }
-
-    private void handleNetworkLost(@NonNull Network network) {
-        mVcnContext.ensureRunningOnLooperThread();
-
-        if (!isSameNetwork(mRecordInProgress, network)) {
-            Slog.wtf(TAG, "Non-underlying Network lost");
-            return;
+    private static boolean isOpportunistic(
+            @NonNull TelephonySubscriptionSnapshot snapshot, Set<Integer> subIds) {
+        if (snapshot == null) {
+            Slog.wtf(TAG, "Got null snapshot");
+            return false;
         }
 
-        mRecordInProgress = null;
-        mCurrentRecord = null;
-        mCb.onSelectedUnderlyingNetworkChanged(null /* underlyingNetworkRecord */);
-    }
-
-    private void handleCapabilitiesChanged(
-            @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
-        mVcnContext.ensureRunningOnLooperThread();
-
-        if (!isSameNetwork(mRecordInProgress, network)) {
-            Slog.wtf(TAG, "Invalid update to NetworkCapabilities");
-            return;
+        for (int subId : subIds) {
+            if (snapshot.isOpportunistic(subId)) {
+                return true;
+            }
         }
 
-        mRecordInProgress.setNetworkCapabilities(networkCapabilities);
-
-        maybeNotifyCallback();
-    }
-
-    private void handlePropertiesChanged(
-            @NonNull Network network, @NonNull LinkProperties linkProperties) {
-        mVcnContext.ensureRunningOnLooperThread();
-
-        if (!isSameNetwork(mRecordInProgress, network)) {
-            Slog.wtf(TAG, "Invalid update to LinkProperties");
-            return;
-        }
-
-        mRecordInProgress.setLinkProperties(linkProperties);
-
-        maybeNotifyCallback();
-    }
-
-    private void handleNetworkBlocked(@NonNull Network network, boolean isBlocked) {
-        mVcnContext.ensureRunningOnLooperThread();
-
-        if (!isSameNetwork(mRecordInProgress, network)) {
-            Slog.wtf(TAG, "Invalid update to isBlocked");
-            return;
-        }
-
-        mRecordInProgress.setIsBlocked(isBlocked);
-
-        maybeNotifyCallback();
+        return false;
     }
 
     /**
@@ -331,36 +450,104 @@
      * truth.
      */
     @VisibleForTesting
-    class RouteSelectionCallback extends NetworkCallback {
+    class UnderlyingNetworkListener extends NetworkCallback {
+        private final Map<Network, UnderlyingNetworkRecord.Builder>
+                mUnderlyingNetworkRecordBuilders = new ArrayMap<>();
+
+        private List<UnderlyingNetworkRecord> getUnderlyingNetworks() {
+            final List<UnderlyingNetworkRecord> records = new ArrayList<>();
+
+            for (UnderlyingNetworkRecord.Builder builder :
+                    mUnderlyingNetworkRecordBuilders.values()) {
+                if (builder.isValid()) {
+                    records.add(builder.build());
+                }
+            }
+
+            return records;
+        }
+
         @Override
         public void onAvailable(@NonNull Network network) {
-            handleNetworkAvailable(network);
+            mUnderlyingNetworkRecordBuilders.put(
+                    network, new UnderlyingNetworkRecord.Builder(network));
         }
 
         @Override
         public void onLost(@NonNull Network network) {
-            handleNetworkLost(network);
+            mUnderlyingNetworkRecordBuilders.remove(network);
+
+            reevaluateNetworks();
         }
 
         @Override
         public void onCapabilitiesChanged(
                 @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
-            if (networkCapabilities.equals(mRecordInProgress.getNetworkCapabilities())) return;
-            handleCapabilitiesChanged(network, networkCapabilities);
+            final UnderlyingNetworkRecord.Builder builder =
+                    mUnderlyingNetworkRecordBuilders.get(network);
+            if (builder == null) {
+                Slog.wtf(TAG, "Got capabilities change for unknown key: " + network);
+                return;
+            }
+
+            builder.setNetworkCapabilities(networkCapabilities);
+            if (builder.isValid()) {
+                reevaluateNetworks();
+            }
         }
 
         @Override
         public void onLinkPropertiesChanged(
                 @NonNull Network network, @NonNull LinkProperties linkProperties) {
-            handlePropertiesChanged(network, linkProperties);
+            final UnderlyingNetworkRecord.Builder builder =
+                    mUnderlyingNetworkRecordBuilders.get(network);
+            if (builder == null) {
+                Slog.wtf(TAG, "Got link properties change for unknown key: " + network);
+                return;
+            }
+
+            builder.setLinkProperties(linkProperties);
+            if (builder.isValid()) {
+                reevaluateNetworks();
+            }
         }
 
         @Override
         public void onBlockedStatusChanged(@NonNull Network network, boolean isBlocked) {
-            handleNetworkBlocked(network, isBlocked);
+            final UnderlyingNetworkRecord.Builder builder =
+                    mUnderlyingNetworkRecordBuilders.get(network);
+            if (builder == null) {
+                Slog.wtf(TAG, "Got blocked status change for unknown key: " + network);
+                return;
+            }
+
+            builder.setIsBlocked(isBlocked);
+            if (builder.isValid()) {
+                reevaluateNetworks();
+            }
         }
     }
 
+    private static int getWifiEntryRssiThreshold(@Nullable PersistableBundle carrierConfig) {
+        if (carrierConfig != null) {
+            return carrierConfig.getInt(
+                    VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY,
+                    WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT);
+        }
+
+        return WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT;
+    }
+
+    private static int getWifiExitRssiThreshold(@Nullable PersistableBundle carrierConfig) {
+        if (carrierConfig != null) {
+            return carrierConfig.getInt(
+                    VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY,
+                    WIFI_EXIT_RSSI_THRESHOLD_DEFAULT);
+        }
+
+        return WIFI_EXIT_RSSI_THRESHOLD_DEFAULT;
+    }
+
     /** A record of a single underlying network, caching relevant fields. */
     public static class UnderlyingNetworkRecord {
         @NonNull public final Network network;
@@ -397,6 +584,89 @@
             return Objects.hash(network, networkCapabilities, linkProperties, isBlocked);
         }
 
+        /**
+         * Gives networks a priority class, based on the following priorities:
+         *
+         * <ol>
+         *   <li>Opportunistic cellular
+         *   <li>Carrier WiFi, signal strength >= WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT
+         *   <li>Carrier WiFi, active network + signal strength >= WIFI_EXIT_RSSI_THRESHOLD_DEFAULT
+         *   <li>Macro cellular
+         *   <li>Any others
+         * </ol>
+         */
+        private int calculatePriorityClass(
+                ParcelUuid subscriptionGroup,
+                TelephonySubscriptionSnapshot snapshot,
+                UnderlyingNetworkRecord currentlySelected,
+                PersistableBundle carrierConfig) {
+            final NetworkCapabilities caps = networkCapabilities;
+
+            // mRouteSelectionNetworkRequest requires a network be both VALIDATED and NOT_SUSPENDED
+
+            if (isBlocked) {
+                Slog.wtf(TAG, "Network blocked for System Server: " + network);
+                return PRIORITY_ANY;
+            }
+
+            if (caps.hasTransport(TRANSPORT_CELLULAR)
+                    && isOpportunistic(snapshot, caps.getSubscriptionIds())) {
+                // If this carrier is the active data provider, ensure that opportunistic is only
+                // ever prioritized if it is also the active data subscription. This ensures that
+                // if an opportunistic subscription is still in the process of being switched to,
+                // or switched away from, the VCN does not attempt to continue using it against the
+                // decision made at the telephony layer. Failure to do so may result in the modem
+                // switching back and forth.
+                //
+                // Allow the following two cases:
+                // 1. Active subId is NOT in the group that this VCN is supporting
+                // 2. This opportunistic subscription is for the active subId
+                if (!snapshot.getAllSubIdsInGroup(subscriptionGroup)
+                                .contains(SubscriptionManager.getActiveDataSubscriptionId())
+                        || caps.getSubscriptionIds()
+                                .contains(SubscriptionManager.getActiveDataSubscriptionId())) {
+                    return PRIORITY_OPPORTUNISTIC_CELLULAR;
+                }
+            }
+
+            if (caps.hasTransport(TRANSPORT_WIFI)) {
+                if (caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)
+                        && currentlySelected != null
+                        && network.equals(currentlySelected.network)) {
+                    return PRIORITY_WIFI_IN_USE;
+                }
+
+                if (caps.getSignalStrength() >= getWifiEntryRssiThreshold(carrierConfig)) {
+                    return PRIORITY_WIFI_PROSPECTIVE;
+                }
+            }
+
+            // Disallow opportunistic subscriptions from matching PRIORITY_MACRO_CELLULAR, as might
+            // be the case when Default Data SubId (CBRS) != Active Data SubId (MACRO), as might be
+            // the case if the Default Data SubId does not support certain services (eg voice
+            // calling)
+            if (caps.hasTransport(TRANSPORT_CELLULAR)
+                    && !isOpportunistic(snapshot, caps.getSubscriptionIds())) {
+                return PRIORITY_MACRO_CELLULAR;
+            }
+
+            return PRIORITY_ANY;
+        }
+
+        private static Comparator<UnderlyingNetworkRecord> getComparator(
+                ParcelUuid subscriptionGroup,
+                TelephonySubscriptionSnapshot snapshot,
+                UnderlyingNetworkRecord currentlySelected,
+                PersistableBundle carrierConfig) {
+            return (left, right) -> {
+                return Integer.compare(
+                        left.calculatePriorityClass(
+                                subscriptionGroup, snapshot, currentlySelected, carrierConfig),
+                        right.calculatePriorityClass(
+                                subscriptionGroup, snapshot, currentlySelected, carrierConfig));
+            };
+        }
+
         /** Dumps the state of this record for logging and debugging purposes. */
         public void dump(IndentingPrintWriter pw) {
             pw.println("UnderlyingNetworkRecord:");
@@ -418,6 +688,8 @@
             boolean mIsBlocked;
             boolean mWasIsBlockedSet;
 
+            @Nullable private UnderlyingNetworkRecord mCached;
+
             private Builder(@NonNull Network network) {
                 mNetwork = network;
             }
@@ -429,6 +701,7 @@
 
             private void setNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
                 mNetworkCapabilities = networkCapabilities;
+                mCached = null;
             }
 
             @Nullable
@@ -438,11 +711,13 @@
 
             private void setLinkProperties(@NonNull LinkProperties linkProperties) {
                 mLinkProperties = linkProperties;
+                mCached = null;
             }
 
             private void setIsBlocked(boolean isBlocked) {
                 mIsBlocked = isBlocked;
                 mWasIsBlockedSet = true;
+                mCached = null;
             }
 
             private boolean isValid() {
@@ -450,12 +725,30 @@
             }
 
             private UnderlyingNetworkRecord build() {
-                return new UnderlyingNetworkRecord(
-                        mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked);
+                if (!isValid()) {
+                    throw new IllegalArgumentException(
+                            "Called build before UnderlyingNetworkRecord was valid");
+                }
+
+                if (mCached == null) {
+                    mCached =
+                            new UnderlyingNetworkRecord(
+                                    mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked);
+                }
+
+                return mCached;
             }
         }
     }
 
+    private class VcnActiveDataSubscriptionIdListener extends TelephonyCallback
+            implements ActiveDataSubscriptionIdListener {
+        @Override
+        public void onActiveDataSubscriptionIdChanged(int subId) {
+            reevaluateNetworks();
+        }
+    }
+
     /** Callbacks for being notified of the changes in, or to the selected underlying network. */
     public interface UnderlyingNetworkTrackerCallback {
         /**
diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java
index 7399e56..d958222 100644
--- a/services/core/java/com/android/server/vcn/VcnContext.java
+++ b/services/core/java/com/android/server/vcn/VcnContext.java
@@ -31,14 +31,17 @@
     @NonNull private final Context mContext;
     @NonNull private final Looper mLooper;
     @NonNull private final VcnNetworkProvider mVcnNetworkProvider;
+    private final boolean mIsInTestMode;
 
     public VcnContext(
             @NonNull Context context,
             @NonNull Looper looper,
-            @NonNull VcnNetworkProvider vcnNetworkProvider) {
+            @NonNull VcnNetworkProvider vcnNetworkProvider,
+            boolean isInTestMode) {
         mContext = Objects.requireNonNull(context, "Missing context");
         mLooper = Objects.requireNonNull(looper, "Missing looper");
         mVcnNetworkProvider = Objects.requireNonNull(vcnNetworkProvider, "Missing networkProvider");
+        mIsInTestMode = isInTestMode;
     }
 
     @NonNull
@@ -56,6 +59,10 @@
         return mVcnNetworkProvider;
     }
 
+    public boolean isInTestMode() {
+        return mIsInTestMode;
+    }
+
     /**
      * Verifies that the caller is running on the VcnContext Thread.
      *
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 736a6f6..8e3cb25 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -91,7 +91,6 @@
 import static android.os.UserManagerInternal.OWNER_TYPE_DEVICE_OWNER;
 import static android.os.UserManagerInternal.OWNER_TYPE_PROFILE_OWNER;
 import static android.os.UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE;
-import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
 import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
 import static android.provider.Telephony.Carriers.DPC_URI;
 import static android.provider.Telephony.Carriers.ENFORCE_KEY;
@@ -195,6 +194,7 @@
 import android.media.AudioManager;
 import android.media.IAudioService;
 import android.net.ConnectivityManager;
+import android.net.ConnectivitySettingsManager;
 import android.net.IIpConnectivityMetrics;
 import android.net.ProxyInfo;
 import android.net.Uri;
@@ -15566,12 +15566,12 @@
         return context.getResources().getString(R.string.config_managed_provisioning_package);
     }
 
-    private void putPrivateDnsSettings(@Nullable String mode, @Nullable String host) {
+    private void putPrivateDnsSettings(int mode, @Nullable String host) {
         // Set Private DNS settings using system permissions, as apps cannot write
         // to global settings.
         mInjector.binderWithCleanCallingIdentity(() -> {
-            mInjector.settingsGlobalPutString(PRIVATE_DNS_MODE, mode);
-            mInjector.settingsGlobalPutString(PRIVATE_DNS_SPECIFIER, host);
+            ConnectivitySettingsManager.setPrivateDnsMode(mContext, mode);
+            ConnectivitySettingsManager.setPrivateDnsHostname(mContext, host);
         });
     }
 
@@ -15592,7 +15592,8 @@
                     throw new IllegalArgumentException(
                             "Host provided for opportunistic mode, but is not needed.");
                 }
-                putPrivateDnsSettings(ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC, null);
+                putPrivateDnsSettings(ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC,
+                        null);
                 return PRIVATE_DNS_SET_NO_ERROR;
             case PRIVATE_DNS_MODE_PROVIDER_HOSTNAME:
                 if (TextUtils.isEmpty(privateDnsHost)
@@ -15604,7 +15605,7 @@
                 // Connectivity check will have been performed in the DevicePolicyManager before
                 // the call here.
                 putPrivateDnsSettings(
-                        ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME,
+                        ConnectivitySettingsManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME,
                         privateDnsHost);
                 return PRIVATE_DNS_SET_NO_ERROR;
             default:
@@ -15621,13 +15622,13 @@
 
         Objects.requireNonNull(who, "ComponentName is null");
         enforceDeviceOwner(who);
-        final String currentMode = ConnectivityManager.getPrivateDnsMode(mContext);
+        final int currentMode = ConnectivitySettingsManager.getPrivateDnsMode(mContext);
         switch (currentMode) {
-            case ConnectivityManager.PRIVATE_DNS_MODE_OFF:
+            case ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF:
                 return PRIVATE_DNS_MODE_OFF;
-            case ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC:
+            case ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC:
                 return PRIVATE_DNS_MODE_OPPORTUNISTIC;
-            case ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME:
+            case ConnectivitySettingsManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME:
                 return PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
         }
 
diff --git a/services/net/TEST_MAPPING b/services/net/TEST_MAPPING
index 7025dd1..7eca1f9 100644
--- a/services/net/TEST_MAPPING
+++ b/services/net/TEST_MAPPING
@@ -2,6 +2,9 @@
   "imports": [
     {
       "path": "frameworks/base/core/java/android/net"
+    },
+    {
+      "path": "packages/modules/Wifi/framework"
     }
   ]
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index deaeb46..a1bce52 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -374,7 +374,7 @@
                 .setPersisted(true)
                 .setRequiredNetwork(new NetworkRequest.Builder()
                         .addCapability(NET_CAPABILITY_IMS)
-                        .addUnwantedCapability(NET_CAPABILITY_OEM_PAID)
+                        .addForbiddenCapability(NET_CAPABILITY_OEM_PAID)
                         .build())
                 .build());
     }
diff --git a/telecomm/java/android/telecom/CallerInfo.java b/telecomm/java/android/telecom/CallerInfo.java
index 2983e63..a63ee46 100644
--- a/telecomm/java/android/telecom/CallerInfo.java
+++ b/telecomm/java/android/telecom/CallerInfo.java
@@ -406,8 +406,7 @@
         // Change the callerInfo number ONLY if it is an emergency number
         // or if it is the voicemail number.  If it is either, take a
         // shortcut and skip the query.
-        TelephonyManager tm = context.getSystemService(TelephonyManager.class);
-        if (tm.isEmergencyNumber(number)) {
+        if (PhoneNumberUtils.isLocalEmergencyNumber(context, number)) {
             return new CallerInfo().markAsEmergency(context);
         } else if (PhoneNumberUtils.isVoiceMailNumber(null, subId, number)) {
             return new CallerInfo().markAsVoiceMail(context, subId);
diff --git a/telecomm/java/android/telecom/CallerInfoAsyncQuery.java b/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
index a9e1a8f..bf49f3c 100644
--- a/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
+++ b/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
@@ -483,7 +483,16 @@
 
         // check to see if these are recognized numbers, and use shortcuts if we can.
         TelephonyManager tm = context.getSystemService(TelephonyManager.class);
-        if (tm.isEmergencyNumber(number)) {
+        boolean isEmergencyNumber = false;
+        try {
+            isEmergencyNumber = tm.isEmergencyNumber(number);
+        } catch (IllegalStateException ise) {
+            // Ignore the exception that Telephony is not up. Use PhoneNumberUtils API now.
+            // Ideally the PhoneNumberUtils API needs to be removed once the
+            // telphony service not up issue can be fixed (b/187412989)
+            isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(context, number);
+        }
+        if (isEmergencyNumber) {
             cw.event = EVENT_EMERGENCY_NUMBER;
         } else if (PhoneNumberUtils.isVoiceMailNumber(context, subId, number)) {
             cw.event = EVENT_VOICEMAIL_NUMBER;
diff --git a/telephony/java/android/telephony/ims/RcsConfig.java b/telephony/java/android/telephony/ims/RcsConfig.java
index 8a31211..6867c86 100644
--- a/telephony/java/android/telephony/ims/RcsConfig.java
+++ b/telephony/java/android/telephony/ims/RcsConfig.java
@@ -22,10 +22,10 @@
 import android.content.Context;
 import android.database.Cursor;
 import android.os.Build;
-import android.os.Parcel;
-import android.os.Parcelable;
 import android.provider.Telephony.SimInfo;
 import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 
 import com.android.telephony.Rlog;
 
@@ -36,7 +36,9 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
 import java.util.zip.GZIPInputStream;
 import java.util.zip.GZIPOutputStream;
 
@@ -44,23 +46,138 @@
  * RCS config data and methods to process the config
  * @hide
  */
-public final class RcsConfig implements Parcelable {
+public final class RcsConfig {
     private static final String LOG_TAG = "RcsConfig";
     private static final boolean DBG = Build.IS_ENG;
 
-    // Tag for Rcs Volte single registration defined in RCC.07 A.1.6.2
-    private static final String TAG_SINGLE_REGISTRATION = "rcsVolteSingleRegistration";
+    // Tag and attribute defined in RCC.07 A.2
+    private static final String TAG_CHARACTERISTIC = "characteristic";
+    private static final String TAG_PARM = "parm";
+    private static final String ATTRIBUTE_TYPE = "type";
+    private static final String ATTRIBUTE_NAME = "name";
+    private static final String ATTRIBUTE_VALUE = "value";
+    // Keyword for Rcs Volte single registration defined in RCC.07 A.1.6.2
+    private static final String PARM_SINGLE_REGISTRATION = "rcsVolteSingleRegistration";
 
-    private final HashMap<String, String> mValues = new HashMap<>();
+    /**
+     * Characteristic of the RCS provisioning config
+     */
+    public static class Characteristic {
+        private String mType;
+        private final Map<String, String> mParms = new ArrayMap<>();
+        private final Set<Characteristic> mSubs = new ArraySet<>();
+        private final Characteristic mParent;
 
-    private RcsConfig(HashMap<String, String> values) {
-        mValues.putAll(values);
+        private Characteristic(String type, Characteristic parent) {
+            mType = type;
+            mParent = parent;
+        }
+
+        private String getType() {
+            return mType;
+        }
+
+        private Map<String, String> getParms() {
+            return mParms;
+        }
+
+        private Set<Characteristic> getSubs() {
+            return mSubs;
+        }
+
+        private Characteristic getParent() {
+            return mParent;
+        }
+
+        private Characteristic getSubByType(String type) {
+            if (TextUtils.equals(mType, type)) {
+                return this;
+            }
+            Characteristic result = null;
+            for (Characteristic sub : mSubs) {
+                result = sub.getSubByType(type);
+                if (result != null) {
+                    break;
+                }
+            }
+            return result;
+        }
+
+        private boolean hasSubByType(String type) {
+            return getSubByType(type) != null;
+        }
+
+        private String getParmValue(String name) {
+            String value = mParms.get(name);
+            if (value == null) {
+                for (Characteristic sub : mSubs) {
+                    value = sub.getParmValue(name);
+                    if (value != null) {
+                        break;
+                    }
+                }
+            }
+            return value;
+        }
+
+        boolean hasParm(String name) {
+            if (mParms.containsKey(name)) {
+                return true;
+            }
+
+            for (Characteristic sub : mSubs) {
+                if (sub.hasParm(name)) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder();
+            sb.append("[" + mType + "]: ");
+            if (DBG) {
+                sb.append(mParms);
+            }
+            for (Characteristic sub : mSubs) {
+                sb.append("\n");
+                sb.append(sub.toString().replace("\n", "\n\t"));
+            }
+            return sb.toString();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof Characteristic)) {
+                return false;
+            }
+
+            Characteristic o = (Characteristic) obj;
+
+            return TextUtils.equals(mType, o.mType) && mParms.equals(o.mParms)
+                    && mSubs.equals(o.mSubs);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mType, mParms, mSubs);
+        }
     }
 
+    private final Characteristic mRoot;
+    private Characteristic mCurrent;
+    private final byte[] mData;
+
     public RcsConfig(byte[] data) throws IllegalArgumentException {
         if (data == null || data.length == 0) {
             throw new IllegalArgumentException("Empty data");
         }
+        mRoot = new Characteristic(null, null);
+        mCurrent = mRoot;
+        mData = data;
+        Characteristic current = mRoot;
         ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
         try {
             XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
@@ -69,16 +186,51 @@
             xpp.setInput(inputStream, null);
             int eventType = xpp.getEventType();
             String tag = null;
-            while (eventType != XmlPullParser.END_DOCUMENT) {
+            while (eventType != XmlPullParser.END_DOCUMENT && current != null) {
                 if (eventType == XmlPullParser.START_TAG) {
-                    tag = xpp.getName().trim();
-                } else if (eventType == XmlPullParser.END_TAG) {
-                    tag = null;
-                } else if (eventType == XmlPullParser.TEXT) {
-                    String value = xpp.getText().trim();
-                    if (!TextUtils.isEmpty(tag) && !TextUtils.isEmpty(value)) {
-                        mValues.put(tag, value);
+                    tag = xpp.getName().trim().toLowerCase();
+                    if (TAG_CHARACTERISTIC.equals(tag)) {
+                        int count = xpp.getAttributeCount();
+                        String type = null;
+                        if (count > 0) {
+                            for (int i = 0; i < count; i++) {
+                                String name = xpp.getAttributeName(i).trim().toLowerCase();
+                                if (ATTRIBUTE_TYPE.equals(name)) {
+                                    type = xpp.getAttributeValue(xpp.getAttributeNamespace(i),
+                                            name).trim().toLowerCase();
+                                    break;
+                                }
+                            }
+                        }
+                        Characteristic next = new Characteristic(type, current);
+                        current.getSubs().add(next);
+                        current = next;
+                    } else if (TAG_PARM.equals(tag)) {
+                        int count = xpp.getAttributeCount();
+                        String key = null;
+                        String value = null;
+                        if (count > 1) {
+                            for (int i = 0; i < count; i++) {
+                                String name = xpp.getAttributeName(i).trim().toLowerCase();
+                                if (ATTRIBUTE_NAME.equals(name)) {
+                                    key = xpp.getAttributeValue(xpp.getAttributeNamespace(i),
+                                            name).trim().toLowerCase();
+                                } else if (ATTRIBUTE_VALUE.equals(name)) {
+                                    value = xpp.getAttributeValue(xpp.getAttributeNamespace(i),
+                                            name).trim();
+                                }
+                            }
+                        }
+                        if (key != null && value != null) {
+                            current.getParms().put(key, value);
+                        }
                     }
+                } else if (eventType == XmlPullParser.END_TAG) {
+                    tag = xpp.getName().trim().toLowerCase();
+                    if (TAG_CHARACTERISTIC.equals(tag)) {
+                        current = current.getParent();
+                    }
+                    tag = null;
                 }
                 eventType = xpp.next();
             }
@@ -102,7 +254,8 @@
      * @return Returns the config value if it exists, or defaultVal.
      */
     public @Nullable String getString(@NonNull String tag, @Nullable String defaultVal) {
-        return mValues.containsKey(tag) ? mValues.get(tag) : defaultVal;
+        String value = mCurrent.getParmValue(tag.trim().toLowerCase());
+        return value != null ?  value : defaultVal;
     }
 
     /**
@@ -115,7 +268,7 @@
      */
     public int getInteger(@NonNull String tag, int defaultVal) {
         try {
-            return Integer.parseInt(mValues.get(tag));
+            return Integer.parseInt(getString(tag, null));
         } catch (NumberFormatException e) {
             logd("error to getInteger for " + tag + " due to " + e);
         }
@@ -131,10 +284,8 @@
      * @return Returns the config value if it exists, or defaultVal.
      */
     public boolean getBoolean(@NonNull String tag, boolean defaultVal) {
-        if (!mValues.containsKey(tag)) {
-            return defaultVal;
-        }
-        return Boolean.parseBoolean(mValues.get(tag));
+        String value = getString(tag, null);
+        return value != null ? Boolean.parseBoolean(value) : defaultVal;
     }
 
     /**
@@ -145,15 +296,70 @@
      * @return Returns true if it exists, or false.
      */
     public boolean hasConfig(@NonNull String tag) {
-        return mValues.containsKey(tag);
+        return mCurrent.hasParm(tag.trim().toLowerCase());
+    }
+
+    /**
+     * Return the Characteristic with the given type
+     */
+    public @Nullable Characteristic getCharacteristic(@NonNull String type) {
+        return mCurrent.getSubByType(type.trim().toLowerCase());
+    }
+
+    /**
+     * Check whether the Characteristic with the given type exists
+     */
+    public boolean hasCharacteristic(@NonNull String type) {
+        return mCurrent.getSubByType(type.trim().toLowerCase()) != null;
+    }
+
+    /**
+     * Set current Characteristic to given Characteristic
+     */
+    public void setCurrentCharacteristic(@NonNull Characteristic current) {
+        if (current != null) {
+            mCurrent = current;
+        }
+    }
+
+    /**
+     * Move current Characteristic to parent layer
+     */
+    public boolean moveToParent() {
+        if (mCurrent.getParent() == null) {
+            return false;
+        }
+        mCurrent = mCurrent.getParent();
+        return true;
+    }
+
+    /**
+     * Move current Characteristic to the root
+     */
+    public void moveToRoot() {
+        mCurrent = mRoot;
+    }
+
+    /**
+     * Return root Characteristic
+     */
+    public @NonNull Characteristic getRoot() {
+        return mRoot;
+    }
+
+    /**
+     * Return current Characteristic
+     */
+    public @NonNull Characteristic getCurrentCharacteristic() {
+        return mCurrent;
     }
 
     /**
      * Check whether Rcs Volte single registration is supported by the config.
      */
     public boolean isRcsVolteSingleRegistrationSupported() {
-        return getBoolean(TAG_SINGLE_REGISTRATION, false)
-                || getInteger(TAG_SINGLE_REGISTRATION, 0) != 0;
+        return getBoolean(PARM_SINGLE_REGISTRATION, false)
+                || getInteger(PARM_SINGLE_REGISTRATION, 0) != 0;
     }
 
     @Override
@@ -161,12 +367,10 @@
         final StringBuilder sb = new StringBuilder();
         sb.append("[RCS Config]");
         if (DBG) {
-            mValues.forEach((t, v) -> {
-                sb.append("\n");
-                sb.append(t);
-                sb.append(" : ");
-                sb.append(v);
-            });
+            sb.append("=== Root ===\n");
+            sb.append(mRoot);
+            sb.append("=== Current ===\n");
+            sb.append(mCurrent);
         }
         return sb.toString();
     }
@@ -179,12 +383,12 @@
 
         RcsConfig other = (RcsConfig) obj;
 
-        return mValues.equals(other.mValues);
+        return mRoot.equals(other.mRoot) && mCurrent.equals(other.mCurrent);
     }
 
     @Override
     public int hashCode() {
-        return mValues.hashCode();
+        return Objects.hash(mRoot, mCurrent);
     }
 
     /**
@@ -275,38 +479,6 @@
         return isCompressed ? data : decompressGzip(data);
     }
 
-    /**
-     * {@link Parcelable#writeToParcel}
-     */
-    public void writeToParcel(@NonNull Parcel out, int flags) {
-        out.writeMap(mValues);
-    }
-
-    /**
-     * {@link Parcelable.Creator}
-     *
-     */
-    public static final @NonNull Parcelable.Creator<RcsConfig>
-            CREATOR = new Creator<RcsConfig>() {
-                @Override
-                public RcsConfig createFromParcel(Parcel in) {
-                    HashMap<String, String> values = in.readHashMap(null);
-                    return values == null ? null : new RcsConfig(values);
-                }
-
-                @Override
-                public RcsConfig[] newArray(int size) {
-                    return new RcsConfig[size];
-                }
-            };
-
-    /**
-     * {@link Parcelable#describeContents}
-     */
-    public int describeContents() {
-        return 0;
-    }
-
     private static void logd(String msg) {
         Rlog.d(LOG_TAG, msg);
     }
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 9410886..c59dcf8 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -16,13 +16,17 @@
 
 package android.net.vcn;
 
+import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
+
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import android.net.NetworkCapabilities;
+import android.net.ipsec.ike.IkeSessionParams;
 import android.net.ipsec.ike.IkeTunnelConnectionParams;
+import android.net.vcn.persistablebundleutils.IkeSessionParamsUtilsTest;
 import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtilsTest;
 
 import androidx.test.filters.SmallTest;
@@ -120,6 +124,21 @@
     }
 
     @Test
+    public void testBuilderRequiresMobikeEnabled() {
+        try {
+            final IkeSessionParams ikeParams =
+                    IkeSessionParamsUtilsTest.createBuilderMinimum()
+                            .removeIkeOption(IKE_OPTION_MOBIKE)
+                            .build();
+            final IkeTunnelConnectionParams tunnelParams =
+                    TunnelConnectionParamsUtilsTest.buildTestParams(ikeParams);
+            new VcnGatewayConnectionConfig.Builder(GATEWAY_CONNECTION_NAME_PREFIX, tunnelParams);
+            fail("Expected exception due to MOBIKE not enabled");
+        } catch (IllegalArgumentException e) {
+        }
+    }
+
+    @Test
     public void testBuilderRequiresNonEmptyExposedCaps() {
         try {
             newBuilder()
diff --git a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
index 582275d..abae81c 100644
--- a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
@@ -16,15 +16,17 @@
 
 package android.net.vcn;
 
-import static android.net.NetworkCapabilities.REDACT_ALL;
-import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
+import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION;
+import static android.net.NetworkCapabilities.REDACT_NONE;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNull;
 
+import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
+import android.os.Build;
 import android.os.Parcel;
 
 import org.junit.Test;
@@ -39,12 +41,6 @@
     private static final VcnTransportInfo WIFI_UNDERLYING_INFO = new VcnTransportInfo(WIFI_INFO);
 
     @Test
-    public void testRedactionDefaults() {
-        assertEquals(REDACT_ALL, CELL_UNDERLYING_INFO.getRedaction());
-        assertEquals(REDACT_ALL, WIFI_UNDERLYING_INFO.getRedaction());
-    }
-
-    @Test
     public void testGetWifiInfo() {
         assertEquals(WIFI_INFO, WIFI_UNDERLYING_INFO.getWifiInfo());
 
@@ -59,15 +55,19 @@
     }
 
     @Test
-    public void testMakeCopySetsRedactions() {
+    public void testMakeCopyRedactForAccessFineLocation() {
         assertEquals(
-                REDACT_FOR_NETWORK_SETTINGS,
-                ((VcnTransportInfo) CELL_UNDERLYING_INFO.makeCopy(REDACT_FOR_NETWORK_SETTINGS))
-                        .getRedaction());
-        assertEquals(
-                REDACT_FOR_NETWORK_SETTINGS,
-                ((VcnTransportInfo) WIFI_UNDERLYING_INFO.makeCopy(REDACT_FOR_NETWORK_SETTINGS))
-                        .getRedaction());
+                SUB_ID,
+                ((VcnTransportInfo) CELL_UNDERLYING_INFO.makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION))
+                        .getSubId());
+
+        // TODO: remove the if statement when S pushes to AOSP.
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+            assertEquals(
+                    WifiConfiguration.INVALID_NETWORK_ID,
+                    ((VcnTransportInfo) WIFI_UNDERLYING_INFO.makeCopy(
+                            REDACT_FOR_ACCESS_FINE_LOCATION)).getWifiInfo().getNetworkId());
+        }
     }
 
     @Test
@@ -78,35 +78,31 @@
     }
 
     @Test
-    public void testParcelUnparcel() {
-        verifyParcelingIsNull(CELL_UNDERLYING_INFO);
-        verifyParcelingIsNull(WIFI_UNDERLYING_INFO);
-    }
+    public void testApplicableRedactions() {
+        assertEquals(REDACT_NONE, CELL_UNDERLYING_INFO.getApplicableRedactions());
 
-    private void verifyParcelingIsNull(VcnTransportInfo vcnTransportInfo) {
-        // Verify redacted by default
-        Parcel parcel = Parcel.obtain();
-        vcnTransportInfo.writeToParcel(parcel, 0 /* flags */);
-        parcel.setDataPosition(0);
-
-        assertNull(VcnTransportInfo.CREATOR.createFromParcel(parcel));
+        final long wifiRedactions = WIFI_INFO.getApplicableRedactions();
+        assertEquals(wifiRedactions, WIFI_UNDERLYING_INFO.getApplicableRedactions());
     }
 
     @Test
-    public void testParcelUnparcelNotRedactedForSysUi() {
-        verifyParcelingForSysUi(CELL_UNDERLYING_INFO);
-        verifyParcelingForSysUi(WIFI_UNDERLYING_INFO);
+    public void testParcelNotRedactedForSysUi() {
+        VcnTransportInfo cellRedacted = parcelForSysUi(CELL_UNDERLYING_INFO);
+        assertEquals(SUB_ID, cellRedacted.getSubId());
+        VcnTransportInfo wifiRedacted = parcelForSysUi(WIFI_UNDERLYING_INFO);
+        assertEquals(NETWORK_ID, wifiRedacted.getWifiInfo().getNetworkId());
     }
 
-    private void verifyParcelingForSysUi(VcnTransportInfo vcnTransportInfo) {
+    private VcnTransportInfo parcelForSysUi(VcnTransportInfo vcnTransportInfo) {
         // Allow fully unredacted; SysUI will have all the relevant permissions.
-        final VcnTransportInfo unRedacted = (VcnTransportInfo) vcnTransportInfo.makeCopy(0);
+        final VcnTransportInfo unRedacted = (VcnTransportInfo) vcnTransportInfo.makeCopy(
+                REDACT_NONE);
         final Parcel parcel = Parcel.obtain();
         unRedacted.writeToParcel(parcel, 0 /* flags */);
         parcel.setDataPosition(0);
 
         final VcnTransportInfo unparceled = VcnTransportInfo.CREATOR.createFromParcel(parcel);
         assertEquals(vcnTransportInfo, unparceled);
-        assertEquals(REDACT_ALL, unparceled.getRedaction());
+        return unparceled;
     }
 }
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
index 393787f..f385113 100644
--- a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
@@ -52,8 +52,8 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class IkeSessionParamsUtilsTest {
-    // Package private for use in EncryptedTunnelParamsUtilsTest
-    static IkeSessionParams.Builder createBuilderMinimum() {
+    // Public for use in VcnGatewayConnectionConfigTest, EncryptedTunnelParamsUtilsTest
+    public static IkeSessionParams.Builder createBuilderMinimum() {
         final InetAddress serverAddress = InetAddresses.parseNumericAddress("192.0.2.100");
 
         // TODO: b/185941731 Make sure all valid IKE_OPTIONS are added and validated.
@@ -63,6 +63,7 @@
                 .setLocalIdentification(new IkeFqdnIdentification("client.test.android.net"))
                 .setRemoteIdentification(new IkeFqdnIdentification("server.test.android.net"))
                 .addIkeOption(IkeSessionParams.IKE_OPTION_FORCE_PORT_4500)
+                .addIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE)
                 .setAuthPsk("psk".getBytes());
     }
 
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java
index 0c8ad32..f9dc9eb 100644
--- a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 
+import android.net.ipsec.ike.IkeSessionParams;
 import android.net.ipsec.ike.IkeTunnelConnectionParams;
 
 import androidx.test.filters.SmallTest;
@@ -31,9 +32,13 @@
 public class TunnelConnectionParamsUtilsTest {
     // Public for use in VcnGatewayConnectionConfigTest
     public static IkeTunnelConnectionParams buildTestParams() {
+        return buildTestParams(IkeSessionParamsUtilsTest.createBuilderMinimum().build());
+    }
+
+    // Public for use in VcnGatewayConnectionConfigTest
+    public static IkeTunnelConnectionParams buildTestParams(IkeSessionParams params) {
         return new IkeTunnelConnectionParams(
-                IkeSessionParamsUtilsTest.createBuilderMinimum().build(),
-                TunnelModeChildSessionParamsUtilsTest.createBuilderMinimum().build());
+                params, TunnelModeChildSessionParamsUtilsTest.createBuilderMinimum().build());
     }
 
     @Test
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 9ecd82f..3360d40 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -37,6 +37,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.CALLS_REAL_METHODS;
@@ -66,6 +67,7 @@
 import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
 import android.net.vcn.VcnConfig;
 import android.net.vcn.VcnConfigTest;
+import android.net.vcn.VcnGatewayConnectionConfigTest;
 import android.net.vcn.VcnManager;
 import android.net.vcn.VcnUnderlyingNetworkPolicy;
 import android.os.IBinder;
@@ -197,7 +199,8 @@
                 .newVcnContext(
                         eq(mMockContext),
                         eq(mTestLooper.getLooper()),
-                        any(VcnNetworkProvider.class));
+                        any(VcnNetworkProvider.class),
+                        anyBoolean());
         doReturn(mSubscriptionTracker)
                 .when(mMockDeps)
                 .newTelephonySubscriptionTracker(
@@ -371,6 +374,12 @@
         TelephonySubscriptionSnapshot snapshot =
                 triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
         verify(mMockDeps)
+                .newVcnContext(
+                        eq(mMockContext),
+                        eq(mTestLooper.getLooper()),
+                        any(VcnNetworkProvider.class),
+                        anyBoolean());
+        verify(mMockDeps)
                 .newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot), any());
     }
 
@@ -528,6 +537,28 @@
     }
 
     @Test
+    public void testSetVcnConfigTestModeRequiresPermission() throws Exception {
+        doThrow(new SecurityException("Requires MANAGE_TEST_NETWORKS"))
+                .when(mMockContext)
+                .enforceCallingPermission(
+                        eq(android.Manifest.permission.MANAGE_TEST_NETWORKS), any());
+
+        final VcnConfig vcnConfig =
+                new VcnConfig.Builder(mMockContext)
+                        .addGatewayConnectionConfig(
+                                VcnGatewayConnectionConfigTest.buildTestConfig())
+                        .setIsTestModeProfile()
+                        .build();
+
+        try {
+            mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, vcnConfig, TEST_PACKAGE_NAME);
+            fail("Expected exception due to using test-mode without permission");
+        } catch (SecurityException e) {
+            verify(mMockPolicyListener, never()).onPolicyChanged();
+        }
+    }
+
+    @Test
     public void testSetVcnConfigNotifiesStatusCallback() throws Exception {
         triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2));
 
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index 528f240..ca74638 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -88,13 +88,13 @@
     private static final SubscriptionInfo TEST_SUBINFO_2 = mock(SubscriptionInfo.class);
     private static final Map<ParcelUuid, Set<String>> TEST_PRIVILEGED_PACKAGES =
             Collections.singletonMap(TEST_PARCEL_UUID, Collections.singleton(PACKAGE_NAME));
-    private static final Map<Integer, ParcelUuid> TEST_SUBID_TO_GROUP_MAP;
+    private static final Map<Integer, SubscriptionInfo> TEST_SUBID_TO_INFO_MAP;
 
     static {
-        final Map<Integer, ParcelUuid> subIdToGroupMap = new HashMap<>();
-        subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_1, TEST_PARCEL_UUID);
-        subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_2, TEST_PARCEL_UUID);
-        TEST_SUBID_TO_GROUP_MAP = Collections.unmodifiableMap(subIdToGroupMap);
+        final Map<Integer, SubscriptionInfo> subIdToGroupMap = new HashMap<>();
+        subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_1, TEST_SUBINFO_1);
+        subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_2, TEST_SUBINFO_2);
+        TEST_SUBID_TO_INFO_MAP = Collections.unmodifiableMap(subIdToGroupMap);
     }
 
     @NonNull private final Context mContext;
@@ -190,13 +190,13 @@
 
     private TelephonySubscriptionSnapshot buildExpectedSnapshot(
             Map<ParcelUuid, Set<String>> privilegedPackages) {
-        return buildExpectedSnapshot(TEST_SUBID_TO_GROUP_MAP, privilegedPackages);
+        return buildExpectedSnapshot(TEST_SUBID_TO_INFO_MAP, privilegedPackages);
     }
 
     private TelephonySubscriptionSnapshot buildExpectedSnapshot(
-            Map<Integer, ParcelUuid> subIdToGroupMap,
+            Map<Integer, SubscriptionInfo> subIdToInfoMap,
             Map<ParcelUuid, Set<String>> privilegedPackages) {
-        return new TelephonySubscriptionSnapshot(subIdToGroupMap, privilegedPackages);
+        return new TelephonySubscriptionSnapshot(subIdToInfoMap, privilegedPackages);
     }
 
     private void verifyNoActiveSubscriptions() {
@@ -371,7 +371,7 @@
     @Test
     public void testTelephonySubscriptionSnapshotGetGroupForSubId() throws Exception {
         final TelephonySubscriptionSnapshot snapshot =
-                new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, emptyMap());
+                new TelephonySubscriptionSnapshot(TEST_SUBID_TO_INFO_MAP, emptyMap());
 
         assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_1));
         assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_2));
@@ -380,7 +380,7 @@
     @Test
     public void testTelephonySubscriptionSnapshotGetAllSubIdsInGroup() throws Exception {
         final TelephonySubscriptionSnapshot snapshot =
-                new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, emptyMap());
+                new TelephonySubscriptionSnapshot(TEST_SUBID_TO_INFO_MAP, emptyMap());
 
         assertEquals(
                 new ArraySet<>(Arrays.asList(TEST_SUBSCRIPTION_ID_1, TEST_SUBSCRIPTION_ID_2)),
diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
index 8289e85..a36fd79 100644
--- a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -41,12 +42,14 @@
 import android.net.TelephonyNetworkSpecifier;
 import android.os.ParcelUuid;
 import android.os.test.TestLooper;
+import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionInfo;
+import android.telephony.TelephonyManager;
 import android.util.ArraySet;
 
 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 import com.android.server.vcn.UnderlyingNetworkTracker.NetworkBringupCallback;
-import com.android.server.vcn.UnderlyingNetworkTracker.RouteSelectionCallback;
+import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkListener;
 import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
 import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
 
@@ -97,11 +100,13 @@
     @Mock private Context mContext;
     @Mock private VcnNetworkProvider mVcnNetworkProvider;
     @Mock private ConnectivityManager mConnectivityManager;
+    @Mock private TelephonyManager mTelephonyManager;
+    @Mock private CarrierConfigManager mCarrierConfigManager;
     @Mock private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
     @Mock private UnderlyingNetworkTrackerCallback mNetworkTrackerCb;
     @Mock private Network mNetwork;
 
-    @Captor private ArgumentCaptor<RouteSelectionCallback> mRouteSelectionCallbackCaptor;
+    @Captor private ArgumentCaptor<UnderlyingNetworkListener> mUnderlyingNetworkListenerCaptor;
 
     private TestLooper mTestLooper;
     private VcnContext mVcnContext;
@@ -112,14 +117,27 @@
         MockitoAnnotations.initMocks(this);
 
         mTestLooper = new TestLooper();
-        mVcnContext = spy(new VcnContext(mContext, mTestLooper.getLooper(), mVcnNetworkProvider));
-        doNothing().when(mVcnContext).ensureRunningOnLooperThread();
+        mVcnContext =
+                spy(
+                        new VcnContext(
+                                mContext,
+                                mTestLooper.getLooper(),
+                                mVcnNetworkProvider,
+                                false /* isInTestMode */));
+        resetVcnContext();
 
         setupSystemService(
                 mContext,
                 mConnectivityManager,
                 Context.CONNECTIVITY_SERVICE,
                 ConnectivityManager.class);
+        setupSystemService(
+                mContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class);
+        setupSystemService(
+                mContext,
+                mCarrierConfigManager,
+                Context.CARRIER_CONFIG_SERVICE,
+                CarrierConfigManager.class);
 
         when(mSubscriptionSnapshot.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(INITIAL_SUB_IDS);
 
@@ -132,6 +150,11 @@
                         mNetworkTrackerCb);
     }
 
+    private void resetVcnContext() {
+        reset(mVcnContext);
+        doNothing().when(mVcnContext).ensureRunningOnLooperThread();
+    }
+
     private static LinkProperties getLinkPropertiesWithName(String iface) {
         LinkProperties linkProperties = new LinkProperties();
         linkProperties.setInterfaceName(iface);
@@ -149,6 +172,31 @@
         verifyNetworkRequestsRegistered(INITIAL_SUB_IDS);
     }
 
+    @Test
+    public void testNetworkCallbacksRegisteredOnStartupForTestMode() {
+        final ConnectivityManager cm = mock(ConnectivityManager.class);
+        setupSystemService(mContext, cm, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+        final VcnContext vcnContext =
+                new VcnContext(
+                        mContext,
+                        mTestLooper.getLooper(),
+                        mVcnNetworkProvider,
+                        true /* isInTestMode */);
+
+        new UnderlyingNetworkTracker(
+                vcnContext,
+                SUB_GROUP,
+                mSubscriptionSnapshot,
+                Collections.singleton(NetworkCapabilities.NET_CAPABILITY_INTERNET),
+                mNetworkTrackerCb);
+
+        verify(cm)
+                .registerNetworkCallback(
+                        eq(getTestNetworkRequest(INITIAL_SUB_IDS)),
+                        any(UnderlyingNetworkListener.class),
+                        any());
+    }
+
     private void verifyNetworkRequestsRegistered(Set<Integer> expectedSubIds) {
         verify(mConnectivityManager)
                 .requestBackgroundNetwork(
@@ -163,9 +211,20 @@
         }
 
         verify(mConnectivityManager)
-                .requestBackgroundNetwork(
+                .registerNetworkCallback(
                         eq(getRouteSelectionRequest(expectedSubIds)),
-                        any(RouteSelectionCallback.class), any());
+                        any(UnderlyingNetworkListener.class),
+                        any());
+        verify(mConnectivityManager)
+                .registerNetworkCallback(
+                        eq(getWifiEntryRssiThresholdRequest(expectedSubIds)),
+                        any(NetworkBringupCallback.class),
+                        any());
+        verify(mConnectivityManager)
+                .registerNetworkCallback(
+                        eq(getWifiExitRssiThresholdRequest(expectedSubIds)),
+                        any(NetworkBringupCallback.class),
+                        any());
     }
 
     @Test
@@ -180,9 +239,10 @@
         mUnderlyingNetworkTracker.updateSubscriptionSnapshot(subscriptionUpdate);
 
         // verify that initially-filed bringup requests are unregistered (cell + wifi)
-        verify(mConnectivityManager, times(INITIAL_SUB_IDS.size() + 1))
+        verify(mConnectivityManager, times(INITIAL_SUB_IDS.size() + 3))
                 .unregisterNetworkCallback(any(NetworkBringupCallback.class));
-        verify(mConnectivityManager).unregisterNetworkCallback(any(RouteSelectionCallback.class));
+        verify(mConnectivityManager)
+                .unregisterNetworkCallback(any(UnderlyingNetworkListener.class));
         verifyNetworkRequestsRegistered(UPDATED_SUB_IDS);
     }
 
@@ -193,6 +253,24 @@
                 .build();
     }
 
+    private NetworkRequest getWifiEntryRssiThresholdRequest(Set<Integer> netCapsSubIds) {
+        // TODO (b/187991063): Add tests for carrier-config based thresholds
+        return getExpectedRequestBase()
+                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                .setSubscriptionIds(netCapsSubIds)
+                .setSignalStrength(UnderlyingNetworkTracker.WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT)
+                .build();
+    }
+
+    private NetworkRequest getWifiExitRssiThresholdRequest(Set<Integer> netCapsSubIds) {
+        // TODO (b/187991063): Add tests for carrier-config based thresholds
+        return getExpectedRequestBase()
+                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                .setSubscriptionIds(netCapsSubIds)
+                .setSignalStrength(UnderlyingNetworkTracker.WIFI_EXIT_RSSI_THRESHOLD_DEFAULT)
+                .build();
+    }
+
     private NetworkRequest getCellRequestForSubId(int subId) {
         return getExpectedRequestBase()
                 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
@@ -201,7 +279,19 @@
     }
 
     private NetworkRequest getRouteSelectionRequest(Set<Integer> netCapsSubIds) {
-        return getExpectedRequestBase().setSubscriptionIds(netCapsSubIds).build();
+        return getExpectedRequestBase()
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
+                .setSubscriptionIds(netCapsSubIds)
+                .build();
+    }
+
+    private NetworkRequest getTestNetworkRequest(Set<Integer> netCapsSubIds) {
+        return new NetworkRequest.Builder()
+                .clearCapabilities()
+                .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+                .setSubscriptionIds(netCapsSubIds)
+                .build();
     }
 
     private NetworkRequest.Builder getExpectedRequestBase() {
@@ -219,11 +309,12 @@
     public void testTeardown() {
         mUnderlyingNetworkTracker.teardown();
 
-        // Expect 3 NetworkBringupCallbacks to be unregistered: 1 for WiFi and 2 for Cellular (1x
-        // for each subId)
-        verify(mConnectivityManager, times(3))
+        // Expect 5 NetworkBringupCallbacks to be unregistered: 1 for WiFi, 2 for Cellular (1x for
+        // each subId), and 1 for each of the Wifi signal strength thresholds
+        verify(mConnectivityManager, times(5))
                 .unregisterNetworkCallback(any(NetworkBringupCallback.class));
-        verify(mConnectivityManager).unregisterNetworkCallback(any(RouteSelectionCallback.class));
+        verify(mConnectivityManager)
+                .unregisterNetworkCallback(any(UnderlyingNetworkListener.class));
     }
 
     @Test
@@ -256,19 +347,19 @@
         verifyRegistrationOnAvailableAndGetCallback();
     }
 
-    private RouteSelectionCallback verifyRegistrationOnAvailableAndGetCallback() {
+    private UnderlyingNetworkListener verifyRegistrationOnAvailableAndGetCallback() {
         return verifyRegistrationOnAvailableAndGetCallback(INITIAL_NETWORK_CAPABILITIES);
     }
 
-    private RouteSelectionCallback verifyRegistrationOnAvailableAndGetCallback(
+    private UnderlyingNetworkListener verifyRegistrationOnAvailableAndGetCallback(
             NetworkCapabilities networkCapabilities) {
         verify(mConnectivityManager)
-                .requestBackgroundNetwork(
+                .registerNetworkCallback(
                         eq(getRouteSelectionRequest(INITIAL_SUB_IDS)),
-                        mRouteSelectionCallbackCaptor.capture(),
+                        mUnderlyingNetworkListenerCaptor.capture(),
                         any());
 
-        RouteSelectionCallback cb = mRouteSelectionCallbackCaptor.getValue();
+        UnderlyingNetworkListener cb = mUnderlyingNetworkListenerCaptor.getValue();
         cb.onAvailable(mNetwork);
         cb.onCapabilitiesChanged(mNetwork, networkCapabilities);
         cb.onLinkPropertiesChanged(mNetwork, INITIAL_LINK_PROPERTIES);
@@ -286,7 +377,7 @@
 
     @Test
     public void testRecordTrackerCallbackNotifiedForNetworkCapabilitiesChange() {
-        RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+        UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
 
         cb.onCapabilitiesChanged(mNetwork, UPDATED_NETWORK_CAPABILITIES);
 
@@ -301,7 +392,7 @@
 
     @Test
     public void testRecordTrackerCallbackNotifiedForLinkPropertiesChange() {
-        RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+        UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
 
         cb.onLinkPropertiesChanged(mNetwork, UPDATED_LINK_PROPERTIES);
 
@@ -316,7 +407,7 @@
 
     @Test
     public void testRecordTrackerCallbackNotifiedForNetworkSuspended() {
-        RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+        UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
 
         cb.onCapabilitiesChanged(mNetwork, SUSPENDED_NETWORK_CAPABILITIES);
 
@@ -335,7 +426,7 @@
 
     @Test
     public void testRecordTrackerCallbackNotifiedForNetworkResumed() {
-        RouteSelectionCallback cb =
+        UnderlyingNetworkListener cb =
                 verifyRegistrationOnAvailableAndGetCallback(SUSPENDED_NETWORK_CAPABILITIES);
 
         cb.onCapabilitiesChanged(mNetwork, INITIAL_NETWORK_CAPABILITIES);
@@ -355,7 +446,7 @@
 
     @Test
     public void testRecordTrackerCallbackNotifiedForBlocked() {
-        RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+        UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
 
         cb.onBlockedStatusChanged(mNetwork, true /* isBlocked */);
 
@@ -370,7 +461,7 @@
 
     @Test
     public void testRecordTrackerCallbackNotifiedForNetworkLoss() {
-        RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+        UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
 
         cb.onLost(mNetwork);
 
@@ -379,7 +470,7 @@
 
     @Test
     public void testRecordTrackerCallbackIgnoresDuplicateRecord() {
-        RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+        UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
 
         cb.onCapabilitiesChanged(mNetwork, INITIAL_NETWORK_CAPABILITIES);
 
@@ -387,4 +478,6 @@
         // UnderlyingNetworkRecord does not actually change
         verifyNoMoreInteractions(mNetworkTrackerCb);
     }
+
+    // TODO (b/187991063): Add tests for network prioritization
 }
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 1ecb4c9..c747bc0 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -54,6 +54,7 @@
 import android.os.ParcelUuid;
 import android.os.PowerManager;
 import android.os.test.TestLooper;
+import android.telephony.SubscriptionInfo;
 
 import com.android.internal.util.State;
 import com.android.internal.util.WakeupMessage;
@@ -73,6 +74,12 @@
 
 public class VcnGatewayConnectionTestBase {
     protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID());
+    protected static final SubscriptionInfo TEST_SUB_INFO = mock(SubscriptionInfo.class);
+
+    static {
+        doReturn(TEST_SUB_GRP).when(TEST_SUB_INFO).getGroupUuid();
+    }
+
     protected static final InetAddress TEST_DNS_ADDR =
             InetAddresses.parseNumericAddress("2001:DB8:0:1::");
     protected static final InetAddress TEST_DNS_ADDR_2 =
@@ -116,7 +123,7 @@
 
     protected static final TelephonySubscriptionSnapshot TEST_SUBSCRIPTION_SNAPSHOT =
             new TelephonySubscriptionSnapshot(
-                    Collections.singletonMap(TEST_SUB_ID, TEST_SUB_GRP), Collections.EMPTY_MAP);
+                    Collections.singletonMap(TEST_SUB_ID, TEST_SUB_INFO), Collections.EMPTY_MAP);
 
     @NonNull protected final Context mContext;
     @NonNull protected final TestLooper mTestLooper;
diff --git a/tools/aapt/pseudolocalize.cpp b/tools/aapt/pseudolocalize.cpp
index 5c47e0f..4e8dcb1 100644
--- a/tools/aapt/pseudolocalize.cpp
+++ b/tools/aapt/pseudolocalize.cpp
@@ -194,7 +194,8 @@
             break;
           }
         }
-        result.remove(length + ext, 0);
+        // Just keep the first length + ext characters
+        result = String16(result, length + ext);
     }
     return result;
 }