Merge "Updating owners of SystemUI"
diff --git a/.prebuilt_info/OWNERS b/.prebuilt_info/OWNERS
new file mode 100644
index 0000000..eb8b89b
--- /dev/null
+++ b/.prebuilt_info/OWNERS
@@ -0,0 +1 @@
+per-file prebuilt_info_packages_CtsShim_*.asciipb = file:/packages/CtsShim/OWNERS
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
index 29bcfe0..1bd90a8 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7197701"
     target: "CtsShim"
     source_file: "aosp_arm64/CtsShimPriv.apk"
   }
@@ -8,5 +8,5 @@
   version: ""
   version_group: ""
   git_project: "platform/frameworks/base"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
 }
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
index be172e6..544bca02 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7197701"
     target: "CtsShim"
     source_file: "aosp_arm64/CtsShim.apk"
   }
@@ -8,5 +8,5 @@
   version: ""
   version_group: ""
   git_project: "platform/frameworks/base"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
 }
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
index 13eca13..72386bb 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7197701"
     target: "CtsShim"
     source_file: "aosp_x86_64/CtsShimPriv.apk"
   }
@@ -8,5 +8,5 @@
   version: ""
   version_group: ""
   git_project: "platform/frameworks/base"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
 }
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
index 2e863fe..893eac2 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7197701"
     target: "CtsShim"
     source_file: "aosp_x86_64/CtsShim.apk"
   }
@@ -8,5 +8,5 @@
   version: ""
   version_group: ""
   git_project: "platform/frameworks/base"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
 }
diff --git a/Android.bp b/Android.bp
index 22246f2..4709323 100644
--- a/Android.bp
+++ b/Android.bp
@@ -306,8 +306,8 @@
 genrule {
     name: "statslog-telephony-common-java-gen",
     tools: ["stats-log-api-gen"],
-    cmd: "$(location stats-log-api-gen) --java $(out) --module telephony_common"
-        + " --javaPackage com.android.internal.telephony --javaClass TelephonyCommonStatsLog",
+    cmd: "$(location stats-log-api-gen) --java $(out) --module telephony_common" +
+        " --javaPackage com.android.internal.telephony --javaClass TelephonyCommonStatsLog",
     out: ["com/android/internal/telephony/TelephonyCommonStatsLog.java"],
 }
 
@@ -363,6 +363,13 @@
         ":platform-compat-native-aidl",
 
         // AIDL sources from external directories
+        ":android.hardware.security.keymint-V1-java-source",
+        ":android.hardware.security.secureclock-V1-java-source",
+        ":android.security.apc-java-source",
+        ":android.security.authorization-java-source",
+        ":android.security.maintenance-java-source",
+        ":android.security.vpnprofilestore-java-source",
+        ":android.system.keystore2-V1-java-source",
         ":credstore_aidl",
         ":dumpstate_aidl",
         ":framework_native_aidl",
@@ -523,11 +530,6 @@
         "android.hardware.vibrator-V1.1-java",
         "android.hardware.vibrator-V1.2-java",
         "android.hardware.vibrator-V1.3-java",
-        "android.security.apc-java",
-        "android.security.authorization-java",
-        "android.security.usermanager-java",
-        "android.security.vpnprofilestore-java",
-        "android.system.keystore2-V1-java",
         "android.system.suspend.control.internal-java",
         "devicepolicyprotosnano",
 
@@ -605,7 +607,6 @@
         // TODO: remove gps_debug and protolog.conf.json when the build system propagates "required" properly.
         "gps_debug.conf",
         "icu4j-platform-compat-config",
-        "libcore-platform-compat-config",
         "protolog.conf.json.gz",
         "services-platform-compat-config",
         "documents-ui-compat-config",
@@ -685,7 +686,7 @@
     name: "statslog-framework-java-gen",
     tools: ["stats-log-api-gen"],
     cmd: "$(location stats-log-api-gen) --java $(out) --module framework" +
-         " --javaPackage com.android.internal.util --javaClass FrameworkStatsLog --worksource",
+        " --javaPackage com.android.internal.util --javaClass FrameworkStatsLog --worksource",
     out: ["com/android/internal/util/FrameworkStatsLog.java"],
 }
 
@@ -786,7 +787,7 @@
 
 java_library {
     name: "framework-annotations-lib",
-    srcs: [ ":framework-annotations" ],
+    srcs: [":framework-annotations"],
     sdk_version: "core_current",
 }
 
@@ -834,17 +835,20 @@
     ],
 }
 
-// keep these files in sync with the package/Tethering/jarjar-rules.txt for the tethering module.
+// keep these files in sync with the package/Tethering/jarjar-rules.txt and
+// package/Connectivity/jarjar-rules.txt for the tethering module and connectivity module.
 filegroup {
-    name: "framework-tethering-shared-srcs",
+    name: "framework-connectivity-shared-srcs",
     srcs: [
         "core/java/android/util/LocalLog.java",
+        // This should be android.util.IndentingPrintWriter, but it's not available in all branches.
         "core/java/com/android/internal/util/IndentingPrintWriter.java",
         "core/java/com/android/internal/util/IState.java",
         "core/java/com/android/internal/util/MessageUtils.java",
         "core/java/com/android/internal/util/State.java",
         "core/java/com/android/internal/util/StateMachine.java",
         "core/java/com/android/internal/util/TrafficStatsConstants.java",
+        "core/java/com/android/internal/util/WakeupMessage.java",
     ],
 }
 
@@ -1010,7 +1014,6 @@
     },
 }
 
-
 // This is the full proto version of libplatformprotos. It may only
 // be used by test code that is not shipped on the device.
 cc_library {
@@ -1171,7 +1174,7 @@
         "core/java/android/os/RemoteException.java",
         "core/java/android/util/AndroidException.java",
     ],
-    libs: [ "unsupportedappusage" ],
+    libs: ["unsupportedappusage"],
 
     dxflags: ["--core-library"],
     installable: false,
diff --git a/StubLibraries.bp b/StubLibraries.bp
index d866116..6316c4a 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -422,10 +422,6 @@
     name: "hwbinder-stubs-docs",
     srcs: [
         "core/java/android/os/HidlSupport.java",
-        "core/java/android/annotation/IntDef.java",
-        "core/java/android/annotation/IntRange.java",
-        "core/java/android/annotation/NonNull.java",
-        "core/java/android/annotation/SystemApi.java",
         "core/java/android/os/HidlMemory.java",
         "core/java/android/os/HwBinder.java",
         "core/java/android/os/HwBlob.java",
@@ -438,6 +434,7 @@
         "core/java/android/os/RemoteException.java",
         "core/java/android/util/AndroidException.java",
     ],
+    libs: ["framework-annotations-lib"],
     installable: false,
     sdk_version: "core_platform",
     annotations_enabled: true,
@@ -451,6 +448,7 @@
 java_library_static {
     name: "hwbinder.stubs",
     sdk_version: "core_current",
+    libs: ["framework-annotations-lib"],
     srcs: [
         ":hwbinder-stubs-docs",
     ],
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 2b12da2..4b449ef 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -57,5 +57,65 @@
     {
       "name": "ManagedProfileLifecycleStressTest"
     }
-  ]
+  ],
+ "auto-postsubmit": [
+   // Test tag for automotive targets. These are only running in postsubmit so as to harden the
+   // automotive targets to avoid introducing additional test flake and build time. The plan for
+   // presubmit testing for auto is to augment the existing tests to cover auto use cases as well.
+   // Additionally, this tag is used in targeted test suites to limit resource usage on the test
+   // infra during the hardening phase.
+   // TODO: this tag to be removed once the above is no longer an issue.
+   {
+     "name": "FrameworksUiServicesTests",
+     "options": [
+       {
+         "exclude-annotation": "androidx.test.filters.FlakyTest"
+       }
+     ]
+   },
+   {
+     "name": "ExtServicesUnitTests",
+     "options": [
+       {
+         "exclude-annotation": "androidx.test.filters.FlakyTest"
+       }
+     ]
+   },
+   {
+     "name": "TestablesTests",
+     "options": [
+       {
+         "exclude-annotation": "androidx.test.filters.FlakyTest"
+       }
+     ]
+   },
+   {
+     "name": "FrameworksCoreTests",
+     "options": [
+       {
+         "include-annotation": "android.platform.test.annotations.Presubmit"
+       },
+       {
+         "exclude-annotation": "androidx.test.filters.FlakyTest"
+       },
+       {
+         "exclude-annotation": "org.junit.Ignore"
+       }
+     ]
+   },
+   {
+     "name": "FrameworksServicesTests",
+     "options": [
+       {
+         "include-annotation": "android.platform.test.annotations.Presubmit"
+       },
+       {
+         "exclude-annotation": "androidx.test.filters.FlakyTest"
+       },
+       {
+         "exclude-annotation": "org.junit.Ignore"
+       }
+     ]
+   }
+ ]
 }
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 24b8055..a3fa8ac 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -1006,6 +1006,41 @@
             mJobId = jobId;
         }
 
+        /**
+         * Creates a new Builder of JobInfo from an existing instance.
+         * @hide
+         */
+        public Builder(@NonNull JobInfo job) {
+            mJobId = job.getId();
+            mJobService = job.getService();
+            mExtras = job.getExtras();
+            mTransientExtras = job.getTransientExtras();
+            mClipData = job.getClipData();
+            mClipGrantFlags = job.getClipGrantFlags();
+            mPriority = job.getPriority();
+            mFlags = job.getFlags();
+            mConstraintFlags = job.getConstraintFlags();
+            mNetworkRequest = job.getRequiredNetwork();
+            mNetworkDownloadBytes = job.getEstimatedNetworkDownloadBytes();
+            mNetworkUploadBytes = job.getEstimatedNetworkUploadBytes();
+            mTriggerContentUris = job.getTriggerContentUris() != null
+                    ? new ArrayList<>(Arrays.asList(job.getTriggerContentUris())) : null;
+            mTriggerContentUpdateDelay = job.getTriggerContentUpdateDelay();
+            mTriggerContentMaxDelay = job.getTriggerContentMaxDelay();
+            mIsPersisted = job.isPersisted();
+            mMinLatencyMillis = job.getMinLatencyMillis();
+            mMaxExecutionDelayMillis = job.getMaxExecutionDelayMillis();
+            mIsPeriodic = job.isPeriodic();
+            mHasEarlyConstraint = job.hasEarlyConstraint();
+            mHasLateConstraint = job.hasLateConstraint();
+            mIntervalMillis = job.getIntervalMillis();
+            mFlexMillis = job.getFlexMillis();
+            mInitialBackoffMillis = job.getInitialBackoffMillis();
+            // mBackoffPolicySet isn't set but it's fine since this is copying from an already valid
+            // job.
+            mBackoffPolicy = job.getBackoffPolicy();
+        }
+
         /** @hide */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         public Builder setPriority(int priority) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 565ed95..15e5e84 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -31,6 +31,7 @@
 import android.content.ServiceConnection;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -73,11 +74,12 @@
 
     private static final String TAG = "JobServiceContext";
     /** Amount of time a job is allowed to execute for before being considered timed-out. */
-    public static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000;  // 10mins.
+    public static final long EXECUTING_TIMESLICE_MILLIS =
+            10 * 60 * 1000 * Build.HW_TIMEOUT_MULTIPLIER; // 10mins.
     /** Amount of time the JobScheduler waits for the initial service launch+bind. */
-    private static final long OP_BIND_TIMEOUT_MILLIS = 18 * 1000;
+    private static final long OP_BIND_TIMEOUT_MILLIS = 18 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
     /** Amount of time the JobScheduler will wait for a response from an app for a message. */
-    private static final long OP_TIMEOUT_MILLIS = 8 * 1000;
+    private static final long OP_TIMEOUT_MILLIS = 8 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
 
     private static final String[] VERB_STRINGS = {
             "VERB_BINDING", "VERB_STARTING", "VERB_EXECUTING", "VERB_STOPPING", "VERB_FINISHED"
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index d7be259..ea8e7bc 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -28,6 +28,7 @@
 import android.content.ClipData;
 import android.content.ComponentName;
 import android.net.Network;
+import android.net.NetworkRequest;
 import android.net.Uri;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -35,6 +36,7 @@
 import android.text.format.DateFormat;
 import android.util.ArraySet;
 import android.util.Pair;
+import android.util.Range;
 import android.util.Slog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
@@ -52,6 +54,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.function.Predicate;
 
 /**
@@ -486,8 +489,15 @@
             // Later, when we check if a given network satisfies the required
             // network, we need to know the UID that is requesting it, so push
             // our source UID into place.
-            job.getRequiredNetwork().networkCapabilities.setSingleUid(this.sourceUid);
+            final JobInfo.Builder builder = new JobInfo.Builder(job);
+            final NetworkRequest.Builder requestBuilder =
+                    new NetworkRequest.Builder(job.getRequiredNetwork());
+            requestBuilder.setUids(
+                    Collections.singleton(new Range<Integer>(this.sourceUid, this.sourceUid)));
+            builder.setRequiredNetwork(requestBuilder.build());
+            job = builder.build();
         }
+
         final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class);
         mHasMediaBackupExemption = !job.hasLateConstraint() && exemptedMediaUrisOnly
                 && requiresNetwork && this.sourcePackageName.equals(jsi.getMediaBackupPackage());
diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp
index 4e5b3ba..0eff83c 100644
--- a/cmds/app_process/Android.bp
+++ b/cmds/app_process/Android.bp
@@ -22,13 +22,9 @@
 
     multilib: {
         lib32: {
-            // TODO(b/142944043): Remove version script when libsigchain is a DSO.
-            version_script: "version-script32.txt",
             suffix: "32",
         },
         lib64: {
-            // TODO(b/142944043): Remove version script when libsigchain is a DSO.
-            version_script: "version-script64.txt",
             suffix: "64",
         },
     },
@@ -43,6 +39,13 @@
         "libhidlbase",
         "liblog",
         "libnativeloader",
+
+        // Even though app_process doesn't call into libsigchain, we need to
+        // make sure it's in the DT list of app_process, as we want all code
+        // in app_process and the libraries it loads to find libsigchain
+        // symbols before libc symbols.
+        "libsigchain",
+
         "libutils",
 
         // This is a list of libraries that need to be included in order to avoid
@@ -52,8 +55,6 @@
         "libwilhelm",
     ],
 
-    whole_static_libs: ["libsigchain"],
-
     compile_multilib: "both",
 
     cflags: [
diff --git a/cmds/app_process/version-script32.txt b/cmds/app_process/version-script32.txt
deleted file mode 100644
index 70810e0..0000000
--- a/cmds/app_process/version-script32.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-global:
-  EnsureFrontOfChain;
-  AddSpecialSignalHandlerFn;
-  RemoveSpecialSignalHandlerFn;
-  SkipAddSignalHandler;
-  bsd_signal;
-  sigaction;
-  sigaction64;
-  signal;
-  sigprocmask;
-  sigprocmask64;
-local:
-  *;
-};
diff --git a/cmds/app_process/version-script64.txt b/cmds/app_process/version-script64.txt
deleted file mode 100644
index 7bcd76b..0000000
--- a/cmds/app_process/version-script64.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-global:
-  EnsureFrontOfChain;
-  AddSpecialSignalHandlerFn;
-  RemoveSpecialSignalHandlerFn;
-  SkipAddSignalHandler;
-  sigaction;
-  sigaction64;
-  signal;
-  sigprocmask;
-  sigprocmask64;
-local:
-  *;
-};
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 5e88d97d..56c35bc 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -1838,11 +1838,9 @@
 android.ddm.DdmHandleAppName$Names
 android.ddm.DdmHandleAppName
 android.ddm.DdmHandleExit
-android.ddm.DdmHandleHeap
 android.ddm.DdmHandleHello
 android.ddm.DdmHandleNativeHeap
 android.ddm.DdmHandleProfiling
-android.ddm.DdmHandleThread
 android.ddm.DdmHandleViewDebug
 android.ddm.DdmRegister
 android.debug.AdbManager
diff --git a/core/api/current.txt b/core/api/current.txt
index 116ac09..ef06785 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7999,13 +7999,13 @@
   }
 
   public class NetworkStatsManager {
-    method public android.app.usage.NetworkStats queryDetails(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
-    method public android.app.usage.NetworkStats queryDetailsForUid(int, String, long, long, int) throws java.lang.SecurityException;
-    method public android.app.usage.NetworkStats queryDetailsForUidTag(int, String, long, long, int, int) throws java.lang.SecurityException;
-    method public android.app.usage.NetworkStats queryDetailsForUidTagState(int, String, long, long, int, int, int) throws java.lang.SecurityException;
-    method public android.app.usage.NetworkStats querySummary(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
-    method public android.app.usage.NetworkStats.Bucket querySummaryForDevice(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
-    method public android.app.usage.NetworkStats.Bucket querySummaryForUser(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+    method @WorkerThread public android.app.usage.NetworkStats queryDetails(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+    method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUid(int, String, long, long, int) throws java.lang.SecurityException;
+    method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTag(int, String, long, long, int, int) throws java.lang.SecurityException;
+    method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTagState(int, String, long, long, int, int, int) throws java.lang.SecurityException;
+    method @WorkerThread public android.app.usage.NetworkStats querySummary(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+    method @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForDevice(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+    method @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForUser(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
     method public void registerUsageCallback(int, String, long, android.app.usage.NetworkStatsManager.UsageCallback);
     method public void registerUsageCallback(int, String, long, android.app.usage.NetworkStatsManager.UsageCallback, @Nullable android.os.Handler);
     method public void unregisterUsageCallback(android.app.usage.NetworkStatsManager.UsageCallback);
@@ -8689,6 +8689,8 @@
     field public static final String ACTION_NAME_CHANGED = "android.bluetooth.device.action.NAME_CHANGED";
     field public static final String ACTION_PAIRING_REQUEST = "android.bluetooth.device.action.PAIRING_REQUEST";
     field public static final String ACTION_UUID = "android.bluetooth.device.action.UUID";
+    field public static final int ADDRESS_TYPE_PUBLIC = 0; // 0x0
+    field public static final int ADDRESS_TYPE_RANDOM = 1; // 0x1
     field public static final int BOND_BONDED = 12; // 0xc
     field public static final int BOND_BONDING = 11; // 0xb
     field public static final int BOND_NONE = 10; // 0xa
@@ -12137,6 +12139,7 @@
     field public static final String FEATURE_FINGERPRINT = "android.hardware.fingerprint";
     field public static final String FEATURE_FREEFORM_WINDOW_MANAGEMENT = "android.software.freeform_window_management";
     field public static final String FEATURE_GAMEPAD = "android.hardware.gamepad";
+    field public static final String FEATURE_HARDWARE_KEYSTORE = "android.hardware.hardware_keystore";
     field public static final String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors";
     field public static final String FEATURE_HOME_SCREEN = "android.software.home_screen";
     field public static final String FEATURE_IDENTITY_CREDENTIAL_HARDWARE = "android.hardware.identity_credential";
@@ -19852,6 +19855,7 @@
     method public android.media.AudioDeviceInfo getPreferredDevice();
     method public android.media.AudioDeviceInfo getRoutedDevice();
     method public int getSampleRate();
+    method @IntRange(from=1) public int getStartThresholdInFrames();
     method public int getState();
     method public int getStreamType();
     method public boolean getTimestamp(android.media.AudioTimestamp);
@@ -19882,6 +19886,7 @@
     method public int setPositionNotificationPeriod(int);
     method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
     method public int setPresentation(@NonNull android.media.AudioPresentation);
+    method @IntRange(from=1) public int setStartThresholdInFrames(@IntRange(from=1) int);
     method @Deprecated protected void setState(int);
     method @Deprecated public int setStereoVolume(float, float);
     method public int setVolume(float);
@@ -20441,7 +20446,9 @@
     method @NonNull public android.media.MediaFormat getOutputFormat(int);
     method @NonNull public android.media.MediaCodec.OutputFrame getOutputFrame(int);
     method @Nullable public android.media.Image getOutputImage(int);
+    method @Nullable public android.media.MediaCodec.ParameterDescriptor getParameterDescriptor(@NonNull String);
     method @NonNull public android.media.MediaCodec.QueueRequest getQueueRequest(int);
+    method @NonNull public java.util.List<java.lang.String> getSupportedVendorParameters();
     method @Nullable public static android.media.Image mapHardwareBuffer(@NonNull android.hardware.HardwareBuffer);
     method public void queueInputBuffer(int, int, int, long, int) throws android.media.MediaCodec.CryptoException;
     method public void queueSecureInputBuffer(int, int, @NonNull android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException;
@@ -20460,6 +20467,8 @@
     method public void signalEndOfInputStream();
     method public void start();
     method public void stop();
+    method public void subscribeToVendorParameters(@NonNull java.util.List<java.lang.String>);
+    method public void unsubscribeFromVendorParameters(@NonNull java.util.List<java.lang.String>);
     field public static final int BUFFER_FLAG_CODEC_CONFIG = 2; // 0x2
     field public static final int BUFFER_FLAG_END_OF_STREAM = 4; // 0x4
     field public static final int BUFFER_FLAG_KEY_FRAME = 1; // 0x1
@@ -20581,6 +20590,11 @@
     method public long getPresentationTimeUs();
   }
 
+  public static class MediaCodec.ParameterDescriptor {
+    method @NonNull public String getName();
+    method public int getType();
+  }
+
   public final class MediaCodec.QueueRequest {
     method public void queue();
     method @NonNull public android.media.MediaCodec.QueueRequest setByteBufferParameter(@NonNull String, @NonNull java.nio.ByteBuffer);
@@ -25018,10 +25032,6 @@
     ctor public NetworkSpecifier();
   }
 
-  public class ParseException extends java.lang.RuntimeException {
-    field public String response;
-  }
-
   public abstract class PlatformVpnProfile {
     method public final int getType();
     method @NonNull public final String getTypeString();
@@ -25433,236 +25443,236 @@
 
 package android.net.rtp {
 
-  public class AudioCodec {
-    method public static android.net.rtp.AudioCodec getCodec(int, String, String);
-    method public static android.net.rtp.AudioCodec[] getCodecs();
-    field public static final android.net.rtp.AudioCodec AMR;
-    field public static final android.net.rtp.AudioCodec GSM;
-    field public static final android.net.rtp.AudioCodec GSM_EFR;
-    field public static final android.net.rtp.AudioCodec PCMA;
-    field public static final android.net.rtp.AudioCodec PCMU;
-    field public final String fmtp;
-    field public final String rtpmap;
-    field public final int type;
+  @Deprecated public class AudioCodec {
+    method @Deprecated public static android.net.rtp.AudioCodec getCodec(int, String, String);
+    method @Deprecated public static android.net.rtp.AudioCodec[] getCodecs();
+    field @Deprecated public static final android.net.rtp.AudioCodec AMR;
+    field @Deprecated public static final android.net.rtp.AudioCodec GSM;
+    field @Deprecated public static final android.net.rtp.AudioCodec GSM_EFR;
+    field @Deprecated public static final android.net.rtp.AudioCodec PCMA;
+    field @Deprecated public static final android.net.rtp.AudioCodec PCMU;
+    field @Deprecated public final String fmtp;
+    field @Deprecated public final String rtpmap;
+    field @Deprecated public final int type;
   }
 
-  public class AudioGroup {
+  @Deprecated public class AudioGroup {
     ctor @Deprecated public AudioGroup();
-    ctor public AudioGroup(@NonNull android.content.Context);
-    method public void clear();
-    method public int getMode();
-    method public android.net.rtp.AudioStream[] getStreams();
-    method public void sendDtmf(int);
-    method public void setMode(int);
-    field public static final int MODE_ECHO_SUPPRESSION = 3; // 0x3
-    field public static final int MODE_MUTED = 1; // 0x1
-    field public static final int MODE_NORMAL = 2; // 0x2
-    field public static final int MODE_ON_HOLD = 0; // 0x0
+    ctor @Deprecated public AudioGroup(@NonNull android.content.Context);
+    method @Deprecated public void clear();
+    method @Deprecated public int getMode();
+    method @Deprecated public android.net.rtp.AudioStream[] getStreams();
+    method @Deprecated public void sendDtmf(int);
+    method @Deprecated public void setMode(int);
+    field @Deprecated public static final int MODE_ECHO_SUPPRESSION = 3; // 0x3
+    field @Deprecated public static final int MODE_MUTED = 1; // 0x1
+    field @Deprecated public static final int MODE_NORMAL = 2; // 0x2
+    field @Deprecated public static final int MODE_ON_HOLD = 0; // 0x0
   }
 
-  public class AudioStream extends android.net.rtp.RtpStream {
-    ctor public AudioStream(java.net.InetAddress) throws java.net.SocketException;
-    method public android.net.rtp.AudioCodec getCodec();
-    method public int getDtmfType();
-    method public android.net.rtp.AudioGroup getGroup();
-    method public final boolean isBusy();
-    method public void join(android.net.rtp.AudioGroup);
-    method public void setCodec(android.net.rtp.AudioCodec);
-    method public void setDtmfType(int);
+  @Deprecated public class AudioStream extends android.net.rtp.RtpStream {
+    ctor @Deprecated public AudioStream(java.net.InetAddress) throws java.net.SocketException;
+    method @Deprecated public android.net.rtp.AudioCodec getCodec();
+    method @Deprecated public int getDtmfType();
+    method @Deprecated public android.net.rtp.AudioGroup getGroup();
+    method @Deprecated public final boolean isBusy();
+    method @Deprecated public void join(android.net.rtp.AudioGroup);
+    method @Deprecated public void setCodec(android.net.rtp.AudioCodec);
+    method @Deprecated public void setDtmfType(int);
   }
 
-  public class RtpStream {
-    method public void associate(java.net.InetAddress, int);
-    method public java.net.InetAddress getLocalAddress();
-    method public int getLocalPort();
-    method public int getMode();
-    method public java.net.InetAddress getRemoteAddress();
-    method public int getRemotePort();
-    method public boolean isBusy();
-    method public void release();
-    method public void setMode(int);
-    field public static final int MODE_NORMAL = 0; // 0x0
-    field public static final int MODE_RECEIVE_ONLY = 2; // 0x2
-    field public static final int MODE_SEND_ONLY = 1; // 0x1
+  @Deprecated public class RtpStream {
+    method @Deprecated public void associate(java.net.InetAddress, int);
+    method @Deprecated public java.net.InetAddress getLocalAddress();
+    method @Deprecated public int getLocalPort();
+    method @Deprecated public int getMode();
+    method @Deprecated public java.net.InetAddress getRemoteAddress();
+    method @Deprecated public int getRemotePort();
+    method @Deprecated public boolean isBusy();
+    method @Deprecated public void release();
+    method @Deprecated public void setMode(int);
+    field @Deprecated public static final int MODE_NORMAL = 0; // 0x0
+    field @Deprecated public static final int MODE_RECEIVE_ONLY = 2; // 0x2
+    field @Deprecated public static final int MODE_SEND_ONLY = 1; // 0x1
   }
 
 }
 
 package android.net.sip {
 
-  public class SipAudioCall {
-    ctor public SipAudioCall(android.content.Context, android.net.sip.SipProfile);
-    method public void answerCall(int) throws android.net.sip.SipException;
-    method public void attachCall(android.net.sip.SipSession, String) throws android.net.sip.SipException;
-    method public void close();
-    method public void continueCall(int) throws android.net.sip.SipException;
-    method public void endCall() throws android.net.sip.SipException;
-    method public android.net.sip.SipProfile getLocalProfile();
-    method public android.net.sip.SipProfile getPeerProfile();
-    method public int getState();
-    method public void holdCall(int) throws android.net.sip.SipException;
-    method public boolean isInCall();
-    method public boolean isMuted();
-    method public boolean isOnHold();
-    method public void makeCall(android.net.sip.SipProfile, android.net.sip.SipSession, int) throws android.net.sip.SipException;
-    method public void sendDtmf(int);
-    method public void sendDtmf(int, android.os.Message);
-    method public void setListener(android.net.sip.SipAudioCall.Listener);
-    method public void setListener(android.net.sip.SipAudioCall.Listener, boolean);
-    method public void setSpeakerMode(boolean);
-    method public void startAudio();
-    method public void toggleMute();
+  @Deprecated public class SipAudioCall {
+    ctor @Deprecated public SipAudioCall(android.content.Context, android.net.sip.SipProfile);
+    method @Deprecated public void answerCall(int) throws android.net.sip.SipException;
+    method @Deprecated public void attachCall(android.net.sip.SipSession, String) throws android.net.sip.SipException;
+    method @Deprecated public void close();
+    method @Deprecated public void continueCall(int) throws android.net.sip.SipException;
+    method @Deprecated public void endCall() throws android.net.sip.SipException;
+    method @Deprecated public android.net.sip.SipProfile getLocalProfile();
+    method @Deprecated public android.net.sip.SipProfile getPeerProfile();
+    method @Deprecated public int getState();
+    method @Deprecated public void holdCall(int) throws android.net.sip.SipException;
+    method @Deprecated public boolean isInCall();
+    method @Deprecated public boolean isMuted();
+    method @Deprecated public boolean isOnHold();
+    method @Deprecated public void makeCall(android.net.sip.SipProfile, android.net.sip.SipSession, int) throws android.net.sip.SipException;
+    method @Deprecated public void sendDtmf(int);
+    method @Deprecated public void sendDtmf(int, android.os.Message);
+    method @Deprecated public void setListener(android.net.sip.SipAudioCall.Listener);
+    method @Deprecated public void setListener(android.net.sip.SipAudioCall.Listener, boolean);
+    method @Deprecated public void setSpeakerMode(boolean);
+    method @Deprecated public void startAudio();
+    method @Deprecated public void toggleMute();
   }
 
-  public static class SipAudioCall.Listener {
-    ctor public SipAudioCall.Listener();
-    method public void onCallBusy(android.net.sip.SipAudioCall);
-    method public void onCallEnded(android.net.sip.SipAudioCall);
-    method public void onCallEstablished(android.net.sip.SipAudioCall);
-    method public void onCallHeld(android.net.sip.SipAudioCall);
-    method public void onCalling(android.net.sip.SipAudioCall);
-    method public void onChanged(android.net.sip.SipAudioCall);
-    method public void onError(android.net.sip.SipAudioCall, int, String);
-    method public void onReadyToCall(android.net.sip.SipAudioCall);
-    method public void onRinging(android.net.sip.SipAudioCall, android.net.sip.SipProfile);
-    method public void onRingingBack(android.net.sip.SipAudioCall);
+  @Deprecated public static class SipAudioCall.Listener {
+    ctor @Deprecated public SipAudioCall.Listener();
+    method @Deprecated public void onCallBusy(android.net.sip.SipAudioCall);
+    method @Deprecated public void onCallEnded(android.net.sip.SipAudioCall);
+    method @Deprecated public void onCallEstablished(android.net.sip.SipAudioCall);
+    method @Deprecated public void onCallHeld(android.net.sip.SipAudioCall);
+    method @Deprecated public void onCalling(android.net.sip.SipAudioCall);
+    method @Deprecated public void onChanged(android.net.sip.SipAudioCall);
+    method @Deprecated public void onError(android.net.sip.SipAudioCall, int, String);
+    method @Deprecated public void onReadyToCall(android.net.sip.SipAudioCall);
+    method @Deprecated public void onRinging(android.net.sip.SipAudioCall, android.net.sip.SipProfile);
+    method @Deprecated public void onRingingBack(android.net.sip.SipAudioCall);
   }
 
-  public class SipErrorCode {
-    method public static String toString(int);
-    field public static final int CLIENT_ERROR = -4; // 0xfffffffc
-    field public static final int CROSS_DOMAIN_AUTHENTICATION = -11; // 0xfffffff5
-    field public static final int DATA_CONNECTION_LOST = -10; // 0xfffffff6
-    field public static final int INVALID_CREDENTIALS = -8; // 0xfffffff8
-    field public static final int INVALID_REMOTE_URI = -6; // 0xfffffffa
-    field public static final int IN_PROGRESS = -9; // 0xfffffff7
-    field public static final int NO_ERROR = 0; // 0x0
-    field public static final int PEER_NOT_REACHABLE = -7; // 0xfffffff9
-    field public static final int SERVER_ERROR = -2; // 0xfffffffe
-    field public static final int SERVER_UNREACHABLE = -12; // 0xfffffff4
-    field public static final int SOCKET_ERROR = -1; // 0xffffffff
-    field public static final int TIME_OUT = -5; // 0xfffffffb
-    field public static final int TRANSACTION_TERMINTED = -3; // 0xfffffffd
+  @Deprecated public class SipErrorCode {
+    method @Deprecated public static String toString(int);
+    field @Deprecated public static final int CLIENT_ERROR = -4; // 0xfffffffc
+    field @Deprecated public static final int CROSS_DOMAIN_AUTHENTICATION = -11; // 0xfffffff5
+    field @Deprecated public static final int DATA_CONNECTION_LOST = -10; // 0xfffffff6
+    field @Deprecated public static final int INVALID_CREDENTIALS = -8; // 0xfffffff8
+    field @Deprecated public static final int INVALID_REMOTE_URI = -6; // 0xfffffffa
+    field @Deprecated public static final int IN_PROGRESS = -9; // 0xfffffff7
+    field @Deprecated public static final int NO_ERROR = 0; // 0x0
+    field @Deprecated public static final int PEER_NOT_REACHABLE = -7; // 0xfffffff9
+    field @Deprecated public static final int SERVER_ERROR = -2; // 0xfffffffe
+    field @Deprecated public static final int SERVER_UNREACHABLE = -12; // 0xfffffff4
+    field @Deprecated public static final int SOCKET_ERROR = -1; // 0xffffffff
+    field @Deprecated public static final int TIME_OUT = -5; // 0xfffffffb
+    field @Deprecated public static final int TRANSACTION_TERMINTED = -3; // 0xfffffffd
   }
 
-  public class SipException extends java.lang.Exception {
-    ctor public SipException();
-    ctor public SipException(String);
-    ctor public SipException(String, Throwable);
+  @Deprecated public class SipException extends java.lang.Exception {
+    ctor @Deprecated public SipException();
+    ctor @Deprecated public SipException(String);
+    ctor @Deprecated public SipException(String, Throwable);
   }
 
-  public class SipManager {
-    method public void close(String) throws android.net.sip.SipException;
-    method public android.net.sip.SipSession createSipSession(android.net.sip.SipProfile, android.net.sip.SipSession.Listener) throws android.net.sip.SipException;
-    method public static String getCallId(android.content.Intent);
-    method public static String getOfferSessionDescription(android.content.Intent);
-    method public android.net.sip.SipSession getSessionFor(android.content.Intent) throws android.net.sip.SipException;
-    method public static boolean isApiSupported(android.content.Context);
-    method public static boolean isIncomingCallIntent(android.content.Intent);
-    method public boolean isOpened(String) throws android.net.sip.SipException;
-    method public boolean isRegistered(String) throws android.net.sip.SipException;
-    method public static boolean isSipWifiOnly(android.content.Context);
-    method public static boolean isVoipSupported(android.content.Context);
-    method public android.net.sip.SipAudioCall makeAudioCall(android.net.sip.SipProfile, android.net.sip.SipProfile, android.net.sip.SipAudioCall.Listener, int) throws android.net.sip.SipException;
-    method public android.net.sip.SipAudioCall makeAudioCall(String, String, android.net.sip.SipAudioCall.Listener, int) throws android.net.sip.SipException;
-    method public static android.net.sip.SipManager newInstance(android.content.Context);
-    method public void open(android.net.sip.SipProfile) throws android.net.sip.SipException;
-    method public void open(android.net.sip.SipProfile, android.app.PendingIntent, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
-    method public void register(android.net.sip.SipProfile, int, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
-    method public void setRegistrationListener(String, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
-    method public android.net.sip.SipAudioCall takeAudioCall(android.content.Intent, android.net.sip.SipAudioCall.Listener) throws android.net.sip.SipException;
-    method public void unregister(android.net.sip.SipProfile, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
-    field public static final String EXTRA_CALL_ID = "android:sipCallID";
-    field public static final String EXTRA_OFFER_SD = "android:sipOfferSD";
-    field public static final int INCOMING_CALL_RESULT_CODE = 101; // 0x65
+  @Deprecated public class SipManager {
+    method @Deprecated public void close(String) throws android.net.sip.SipException;
+    method @Deprecated public android.net.sip.SipSession createSipSession(android.net.sip.SipProfile, android.net.sip.SipSession.Listener) throws android.net.sip.SipException;
+    method @Deprecated public static String getCallId(android.content.Intent);
+    method @Deprecated public static String getOfferSessionDescription(android.content.Intent);
+    method @Deprecated public android.net.sip.SipSession getSessionFor(android.content.Intent) throws android.net.sip.SipException;
+    method @Deprecated public static boolean isApiSupported(android.content.Context);
+    method @Deprecated public static boolean isIncomingCallIntent(android.content.Intent);
+    method @Deprecated public boolean isOpened(String) throws android.net.sip.SipException;
+    method @Deprecated public boolean isRegistered(String) throws android.net.sip.SipException;
+    method @Deprecated public static boolean isSipWifiOnly(android.content.Context);
+    method @Deprecated public static boolean isVoipSupported(android.content.Context);
+    method @Deprecated public android.net.sip.SipAudioCall makeAudioCall(android.net.sip.SipProfile, android.net.sip.SipProfile, android.net.sip.SipAudioCall.Listener, int) throws android.net.sip.SipException;
+    method @Deprecated public android.net.sip.SipAudioCall makeAudioCall(String, String, android.net.sip.SipAudioCall.Listener, int) throws android.net.sip.SipException;
+    method @Deprecated public static android.net.sip.SipManager newInstance(android.content.Context);
+    method @Deprecated public void open(android.net.sip.SipProfile) throws android.net.sip.SipException;
+    method @Deprecated public void open(android.net.sip.SipProfile, android.app.PendingIntent, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
+    method @Deprecated public void register(android.net.sip.SipProfile, int, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
+    method @Deprecated public void setRegistrationListener(String, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
+    method @Deprecated public android.net.sip.SipAudioCall takeAudioCall(android.content.Intent, android.net.sip.SipAudioCall.Listener) throws android.net.sip.SipException;
+    method @Deprecated public void unregister(android.net.sip.SipProfile, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
+    field @Deprecated public static final String EXTRA_CALL_ID = "android:sipCallID";
+    field @Deprecated public static final String EXTRA_OFFER_SD = "android:sipOfferSD";
+    field @Deprecated public static final int INCOMING_CALL_RESULT_CODE = 101; // 0x65
   }
 
-  public class SipProfile implements java.lang.Cloneable android.os.Parcelable java.io.Serializable {
-    method public int describeContents();
-    method public String getAuthUserName();
-    method public boolean getAutoRegistration();
-    method public String getDisplayName();
-    method public String getPassword();
-    method public int getPort();
-    method public String getProfileName();
-    method public String getProtocol();
-    method public String getProxyAddress();
-    method public boolean getSendKeepAlive();
-    method public String getSipDomain();
-    method public String getUriString();
-    method public String getUserName();
-    method public void setCallingUid(int);
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.net.sip.SipProfile> CREATOR;
+  @Deprecated public class SipProfile implements java.lang.Cloneable android.os.Parcelable java.io.Serializable {
+    method @Deprecated public int describeContents();
+    method @Deprecated public String getAuthUserName();
+    method @Deprecated public boolean getAutoRegistration();
+    method @Deprecated public String getDisplayName();
+    method @Deprecated public String getPassword();
+    method @Deprecated public int getPort();
+    method @Deprecated public String getProfileName();
+    method @Deprecated public String getProtocol();
+    method @Deprecated public String getProxyAddress();
+    method @Deprecated public boolean getSendKeepAlive();
+    method @Deprecated public String getSipDomain();
+    method @Deprecated public String getUriString();
+    method @Deprecated public String getUserName();
+    method @Deprecated public void setCallingUid(int);
+    method @Deprecated public void writeToParcel(android.os.Parcel, int);
+    field @Deprecated public static final android.os.Parcelable.Creator<android.net.sip.SipProfile> CREATOR;
   }
 
-  public static class SipProfile.Builder {
-    ctor public SipProfile.Builder(android.net.sip.SipProfile);
-    ctor public SipProfile.Builder(String) throws java.text.ParseException;
-    ctor public SipProfile.Builder(String, String) throws java.text.ParseException;
-    method public android.net.sip.SipProfile build();
-    method public android.net.sip.SipProfile.Builder setAuthUserName(String);
-    method public android.net.sip.SipProfile.Builder setAutoRegistration(boolean);
-    method public android.net.sip.SipProfile.Builder setDisplayName(String);
-    method public android.net.sip.SipProfile.Builder setOutboundProxy(String);
-    method public android.net.sip.SipProfile.Builder setPassword(String);
-    method public android.net.sip.SipProfile.Builder setPort(int) throws java.lang.IllegalArgumentException;
-    method public android.net.sip.SipProfile.Builder setProfileName(String);
-    method public android.net.sip.SipProfile.Builder setProtocol(String) throws java.lang.IllegalArgumentException;
-    method public android.net.sip.SipProfile.Builder setSendKeepAlive(boolean);
+  @Deprecated public static class SipProfile.Builder {
+    ctor @Deprecated public SipProfile.Builder(android.net.sip.SipProfile);
+    ctor @Deprecated public SipProfile.Builder(String) throws java.text.ParseException;
+    ctor @Deprecated public SipProfile.Builder(String, String) throws java.text.ParseException;
+    method @Deprecated public android.net.sip.SipProfile build();
+    method @Deprecated public android.net.sip.SipProfile.Builder setAuthUserName(String);
+    method @Deprecated public android.net.sip.SipProfile.Builder setAutoRegistration(boolean);
+    method @Deprecated public android.net.sip.SipProfile.Builder setDisplayName(String);
+    method @Deprecated public android.net.sip.SipProfile.Builder setOutboundProxy(String);
+    method @Deprecated public android.net.sip.SipProfile.Builder setPassword(String);
+    method @Deprecated public android.net.sip.SipProfile.Builder setPort(int) throws java.lang.IllegalArgumentException;
+    method @Deprecated public android.net.sip.SipProfile.Builder setProfileName(String);
+    method @Deprecated public android.net.sip.SipProfile.Builder setProtocol(String) throws java.lang.IllegalArgumentException;
+    method @Deprecated public android.net.sip.SipProfile.Builder setSendKeepAlive(boolean);
   }
 
-  public interface SipRegistrationListener {
-    method public void onRegistering(String);
-    method public void onRegistrationDone(String, long);
-    method public void onRegistrationFailed(String, int, String);
+  @Deprecated public interface SipRegistrationListener {
+    method @Deprecated public void onRegistering(String);
+    method @Deprecated public void onRegistrationDone(String, long);
+    method @Deprecated public void onRegistrationFailed(String, int, String);
   }
 
-  public final class SipSession {
-    method public void answerCall(String, int);
-    method public void changeCall(String, int);
-    method public void endCall();
-    method public String getCallId();
-    method public String getLocalIp();
-    method public android.net.sip.SipProfile getLocalProfile();
-    method public android.net.sip.SipProfile getPeerProfile();
-    method public int getState();
-    method public boolean isInCall();
-    method public void makeCall(android.net.sip.SipProfile, String, int);
-    method public void register(int);
-    method public void setListener(android.net.sip.SipSession.Listener);
-    method public void unregister();
+  @Deprecated public final class SipSession {
+    method @Deprecated public void answerCall(String, int);
+    method @Deprecated public void changeCall(String, int);
+    method @Deprecated public void endCall();
+    method @Deprecated public String getCallId();
+    method @Deprecated public String getLocalIp();
+    method @Deprecated public android.net.sip.SipProfile getLocalProfile();
+    method @Deprecated public android.net.sip.SipProfile getPeerProfile();
+    method @Deprecated public int getState();
+    method @Deprecated public boolean isInCall();
+    method @Deprecated public void makeCall(android.net.sip.SipProfile, String, int);
+    method @Deprecated public void register(int);
+    method @Deprecated public void setListener(android.net.sip.SipSession.Listener);
+    method @Deprecated public void unregister();
   }
 
-  public static class SipSession.Listener {
-    ctor public SipSession.Listener();
-    method public void onCallBusy(android.net.sip.SipSession);
-    method public void onCallChangeFailed(android.net.sip.SipSession, int, String);
-    method public void onCallEnded(android.net.sip.SipSession);
-    method public void onCallEstablished(android.net.sip.SipSession, String);
-    method public void onCalling(android.net.sip.SipSession);
-    method public void onError(android.net.sip.SipSession, int, String);
-    method public void onRegistering(android.net.sip.SipSession);
-    method public void onRegistrationDone(android.net.sip.SipSession, int);
-    method public void onRegistrationFailed(android.net.sip.SipSession, int, String);
-    method public void onRegistrationTimeout(android.net.sip.SipSession);
-    method public void onRinging(android.net.sip.SipSession, android.net.sip.SipProfile, String);
-    method public void onRingingBack(android.net.sip.SipSession);
+  @Deprecated public static class SipSession.Listener {
+    ctor @Deprecated public SipSession.Listener();
+    method @Deprecated public void onCallBusy(android.net.sip.SipSession);
+    method @Deprecated public void onCallChangeFailed(android.net.sip.SipSession, int, String);
+    method @Deprecated public void onCallEnded(android.net.sip.SipSession);
+    method @Deprecated public void onCallEstablished(android.net.sip.SipSession, String);
+    method @Deprecated public void onCalling(android.net.sip.SipSession);
+    method @Deprecated public void onError(android.net.sip.SipSession, int, String);
+    method @Deprecated public void onRegistering(android.net.sip.SipSession);
+    method @Deprecated public void onRegistrationDone(android.net.sip.SipSession, int);
+    method @Deprecated public void onRegistrationFailed(android.net.sip.SipSession, int, String);
+    method @Deprecated public void onRegistrationTimeout(android.net.sip.SipSession);
+    method @Deprecated public void onRinging(android.net.sip.SipSession, android.net.sip.SipProfile, String);
+    method @Deprecated public void onRingingBack(android.net.sip.SipSession);
   }
 
-  public static class SipSession.State {
-    method public static String toString(int);
-    field public static final int DEREGISTERING = 2; // 0x2
-    field public static final int INCOMING_CALL = 3; // 0x3
-    field public static final int INCOMING_CALL_ANSWERING = 4; // 0x4
-    field public static final int IN_CALL = 8; // 0x8
-    field public static final int NOT_DEFINED = 101; // 0x65
-    field public static final int OUTGOING_CALL = 5; // 0x5
-    field public static final int OUTGOING_CALL_CANCELING = 7; // 0x7
-    field public static final int OUTGOING_CALL_RING_BACK = 6; // 0x6
-    field public static final int PINGING = 9; // 0x9
-    field public static final int READY_TO_CALL = 0; // 0x0
-    field public static final int REGISTERING = 1; // 0x1
+  @Deprecated public static class SipSession.State {
+    method @Deprecated public static String toString(int);
+    field @Deprecated public static final int DEREGISTERING = 2; // 0x2
+    field @Deprecated public static final int INCOMING_CALL = 3; // 0x3
+    field @Deprecated public static final int INCOMING_CALL_ANSWERING = 4; // 0x4
+    field @Deprecated public static final int IN_CALL = 8; // 0x8
+    field @Deprecated public static final int NOT_DEFINED = 101; // 0x65
+    field @Deprecated public static final int OUTGOING_CALL = 5; // 0x5
+    field @Deprecated public static final int OUTGOING_CALL_CANCELING = 7; // 0x7
+    field @Deprecated public static final int OUTGOING_CALL_RING_BACK = 6; // 0x6
+    field @Deprecated public static final int PINGING = 9; // 0x9
+    field @Deprecated public static final int READY_TO_CALL = 0; // 0x0
+    field @Deprecated public static final int REGISTERING = 1; // 0x1
   }
 
 }
@@ -25694,17 +25704,14 @@
   public final class VcnGatewayConnectionConfig {
     method @NonNull public int[] getExposedCapabilities();
     method @IntRange(from=android.net.vcn.VcnGatewayConnectionConfig.MIN_MTU_V6) public int getMaxMtu();
-    method @NonNull public int[] getRequiredUnderlyingCapabilities();
     method @NonNull public long[] getRetryInterval();
   }
 
   public static final class VcnGatewayConnectionConfig.Builder {
     ctor public VcnGatewayConnectionConfig.Builder(@NonNull android.net.vcn.VcnControlPlaneConfig);
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addExposedCapability(int);
-    method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addRequiredUnderlyingCapability(int);
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig build();
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int);
-    method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeRequiredUnderlyingCapability(int);
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=android.net.vcn.VcnGatewayConnectionConfig.MIN_MTU_V6) int);
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryInterval(@NonNull long[]);
   }
@@ -25726,7 +25733,7 @@
   public abstract static class VcnManager.VcnStatusCallback {
     ctor public VcnManager.VcnStatusCallback();
     method public abstract void onGatewayConnectionError(@NonNull int[], int, @Nullable Throwable);
-    method public abstract void onVcnStatusChanged(int);
+    method public abstract void onStatusChanged(int);
   }
 
 }
@@ -33594,7 +33601,7 @@
     field public static final String ACTION_LOCATION_SOURCE_SETTINGS = "android.settings.LOCATION_SOURCE_SETTINGS";
     field public static final String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS";
     field public static final String ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION = "android.settings.MANAGE_ALL_FILES_ACCESS_PERMISSION";
-    field public static final String ACTION_MANAGE_ALL_SUBSCRIPTIONS_SETTINGS = "android.settings.MANAGE_ALL_SUBSCRIPTIONS_SETTINGS";
+    field public static final String ACTION_MANAGE_ALL_SIM_PROFILES_SETTINGS = "android.settings.MANAGE_ALL_SIM_PROFILES_SETTINGS";
     field public static final String ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS";
     field public static final String ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION = "android.settings.MANAGE_APP_ALL_FILES_ACCESS_PERMISSION";
     field public static final String ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS";
@@ -34222,6 +34229,9 @@
     method public static android.net.Uri getUriForSubscriptionIdAndField(int, String);
     field public static final String AUTHORITY = "service-state";
     field public static final android.net.Uri CONTENT_URI;
+    field public static final String DATA_NETWORK_TYPE = "data_network_type";
+    field public static final String DATA_REG_STATE = "data_reg_state";
+    field public static final String DUPLEX_MODE = "duplex_mode";
     field public static final String IS_MANUAL_NETWORK_SELECTION = "is_manual_network_selection";
     field public static final String VOICE_OPERATOR_NUMERIC = "voice_operator_numeric";
     field public static final String VOICE_REG_STATE = "voice_reg_state";
@@ -38399,22 +38409,22 @@
     method public final void conferenceRemoteConnections(android.telecom.RemoteConnection, android.telecom.RemoteConnection);
     method public final void connectionServiceFocusReleased();
     method @Nullable public final android.telecom.RemoteConference createRemoteIncomingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
-    method public final android.telecom.RemoteConnection createRemoteIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method @Nullable public final android.telecom.RemoteConnection createRemoteIncomingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.ConnectionRequest);
     method @Nullable public final android.telecom.RemoteConference createRemoteOutgoingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
-    method public final android.telecom.RemoteConnection createRemoteOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method @Nullable public final android.telecom.RemoteConnection createRemoteOutgoingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.ConnectionRequest);
     method public final java.util.Collection<android.telecom.Conference> getAllConferences();
     method public final java.util.Collection<android.telecom.Connection> getAllConnections();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConference(android.telecom.Connection, android.telecom.Connection);
     method public void onConnectionServiceFocusGained();
     method public void onConnectionServiceFocusLost();
-    method @Nullable public android.telecom.Conference onCreateIncomingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
+    method @Nullable public android.telecom.Conference onCreateIncomingConference(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.ConnectionRequest);
     method public void onCreateIncomingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateIncomingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
-    method @Nullable public android.telecom.Conference onCreateOutgoingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
-    method public void onCreateOutgoingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
+    method @Nullable public android.telecom.Conference onCreateOutgoingConference(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.ConnectionRequest);
+    method public void onCreateOutgoingConferenceFailed(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public void onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateOutgoingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
@@ -39099,7 +39109,6 @@
     method public static boolean isConfigForIdentifiedCarrier(android.os.PersistableBundle);
     method public void notifyConfigChangedForSubId(int);
     field public static final String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
-    field public static final int CARRIER_NR_AVAILABILITY_NONE = 0; // 0x0
     field public static final int CARRIER_NR_AVAILABILITY_NSA = 1; // 0x1
     field public static final int CARRIER_NR_AVAILABILITY_SA = 2; // 0x2
     field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
@@ -39160,7 +39169,7 @@
     field public static final String KEY_CARRIER_INSTANT_LETTERING_LENGTH_LIMIT_INT = "carrier_instant_lettering_length_limit_int";
     field public static final String KEY_CARRIER_NAME_OVERRIDE_BOOL = "carrier_name_override_bool";
     field public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
-    field public static final String KEY_CARRIER_NR_AVAILABILITY_INT = "carrier_nr_availability_int";
+    field public static final String KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY = "carrier_nr_availabilities_int_array";
     field public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL = "carrier_rcs_provisioning_required_bool";
     field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string";
     field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
@@ -39229,6 +39238,7 @@
     field public static final String KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY = "gsm_roaming_networks_string_array";
     field public static final String KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL = "has_in_call_noise_suppression_bool";
     field public static final String KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL = "hide_carrier_network_settings_bool";
+    field public static final String KEY_HIDE_ENABLE_2G = "hide_enable_2g_bool";
     field public static final String KEY_HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool";
     field public static final String KEY_HIDE_IMS_APN_BOOL = "hide_ims_apn_bool";
     field public static final String KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL = "hide_lte_plus_data_icon_bool";
@@ -39308,6 +39318,7 @@
     field public static final String KEY_RTT_SUPPORTED_FOR_VT_BOOL = "rtt_supported_for_vt_bool";
     field public static final String KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL = "rtt_supported_while_roaming_bool";
     field public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool";
+    field public static final String KEY_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL = "rtt_upgrade_supported_for_downgraded_vt_call";
     field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
     field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
     field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
@@ -39350,6 +39361,7 @@
     field public static final String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
     field public static final String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
     field public static final String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
+    field public static final String KEY_VT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_RTT_CALL_BOOL = "vt_upgrade_supported_for_downgraded_rtt_call";
     field public static final String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL = "vvm_cellular_data_required_bool";
     field public static final String KEY_VVM_CLIENT_PREFIX_STRING = "vvm_client_prefix_string";
     field public static final String KEY_VVM_DESTINATION_NUMBER_STRING = "vvm_destination_number_string";
@@ -39392,6 +39404,7 @@
     field public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT = "ims.non_rcs_capabilities_cache_expiration_sec_int";
     field public static final String KEY_PREFIX = "ims.";
     field public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL = "ims.rcs_bulk_capability_exchange_bool";
+    field public static final String KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY = "ims.rcs_feature_tag_allowed_string_array";
     field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int";
   }
 
@@ -40663,7 +40676,6 @@
     method public static int[] calculateLength(String, boolean);
     method @Deprecated public static android.telephony.SmsMessage createFromPdu(byte[]);
     method public static android.telephony.SmsMessage createFromPdu(byte[], String);
-    method @Nullable public static android.telephony.SmsMessage createSmsSubmitPdu(@NonNull byte[], boolean);
     method public String getDisplayMessageBody();
     method public String getDisplayOriginatingAddress();
     method public String getEmailBody();
@@ -40909,6 +40921,10 @@
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onMessageWaitingIndicatorChanged(boolean);
   }
 
+  public static interface TelephonyCallback.PhysicalChannelConfigListener {
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPhysicalChannelConfigChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
+  }
+
   public static interface TelephonyCallback.PreciseDataConnectionStateListener {
     method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
   }
@@ -41022,6 +41038,7 @@
     method @Deprecated public String iccTransmitApduBasicChannel(int, int, int, int, int, String);
     method @Deprecated public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String);
     method public boolean isConcurrentVoiceAndDataSupported();
+    method public boolean isDataCapable();
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isDataConnectionAllowed();
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabled();
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabledForReason(int);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 4d9b4aa..ee53be5 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -20,12 +20,47 @@
 
 }
 
+package android.content {
+
+  public abstract class Context {
+    field public static final String TEST_NETWORK_SERVICE = "test_network";
+  }
+
+  public class Intent implements java.lang.Cloneable android.os.Parcelable {
+    field public static final String ACTION_CLEAR_DNS_CACHE = "android.intent.action.CLEAR_DNS_CACHE";
+  }
+
+}
+
 package android.net {
 
+  public final class EthernetNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
+    ctor public EthernetNetworkSpecifier(@NonNull String);
+    method public int describeContents();
+    method @Nullable public String getInterfaceName();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.EthernetNetworkSpecifier> CREATOR;
+  }
+
   public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
     method public int getResourceId();
   }
 
+  public class NetworkPolicyManager {
+    method @NonNull public static String blockedReasonsToString(int);
+    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getMultipathPreference(@NonNull android.net.Network);
+    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getRestrictBackgroundStatus(int);
+    method public static boolean isUidBlocked(int, boolean);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidNetworkingBlocked(int, boolean);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidRestrictedOnMeteredNetworks(int);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void registerNetworkPolicyCallback(@Nullable java.util.concurrent.Executor, @NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void unregisterNetworkPolicyCallback(@NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback);
+  }
+
+  public static interface NetworkPolicyManager.NetworkPolicyCallback {
+    method public default void onUidBlockedReasonChanged(int, int);
+  }
+
   public final class NetworkStateSnapshot implements android.os.Parcelable {
     ctor public NetworkStateSnapshot(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, @Nullable String, int);
     method public int describeContents();
@@ -42,6 +77,16 @@
     method @Nullable public byte[] getWatchlistConfigHash();
   }
 
+  public class PacProxyManager {
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void addPacProxyInstalledListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.PacProxyManager.PacProxyInstalledListener);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void removePacProxyInstalledListener(@NonNull android.net.PacProxyManager.PacProxyInstalledListener);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setCurrentProxyScriptUrl(@Nullable android.net.ProxyInfo);
+  }
+
+  public static interface PacProxyManager.PacProxyInstalledListener {
+    method public void onPacProxyInstalled(@Nullable android.net.Network, @NonNull android.net.ProxyInfo);
+  }
+
   public final class Proxy {
     method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo);
   }
@@ -56,6 +101,14 @@
     field @NonNull public final java.util.List<java.lang.String> underlyingIfaces;
   }
 
+  public class VpnManager {
+    field @Deprecated public static final int TYPE_VPN_LEGACY = 3; // 0x3
+    field public static final int TYPE_VPN_NONE = -1; // 0xffffffff
+    field public static final int TYPE_VPN_OEM = 4; // 0x4
+    field public static final int TYPE_VPN_PLATFORM = 2; // 0x2
+    field public static final int TYPE_VPN_SERVICE = 1; // 0x1
+  }
+
 }
 
 package android.os {
@@ -76,6 +129,10 @@
     method public default int getStability();
   }
 
+  public class Process {
+    field public static final int VPN_UID = 1016; // 0x3f8
+  }
+
   public class StatsServiceManager {
     method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsCompanionServiceRegisterer();
     method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsManagerServiceRegisterer();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index dc2e7a7..4676523 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -113,6 +113,7 @@
     field public static final String LOOP_RADIO = "android.permission.LOOP_RADIO";
     field public static final String MANAGE_ACCESSIBILITY = "android.permission.MANAGE_ACCESSIBILITY";
     field public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
+    field public static final String MANAGE_APP_HIBERNATION = "android.permission.MANAGE_APP_HIBERNATION";
     field public static final String MANAGE_APP_OPS_RESTRICTIONS = "android.permission.MANAGE_APP_OPS_RESTRICTIONS";
     field public static final String MANAGE_APP_PREDICTIONS = "android.permission.MANAGE_APP_PREDICTIONS";
     field public static final String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS";
@@ -160,6 +161,7 @@
     field public static final String OBSERVE_NETWORK_POLICY = "android.permission.OBSERVE_NETWORK_POLICY";
     field public static final String OBSERVE_ROLE_HOLDERS = "android.permission.OBSERVE_ROLE_HOLDERS";
     field public static final String OPEN_ACCESSIBILITY_DETAILS_SETTINGS = "android.permission.OPEN_ACCESSIBILITY_DETAILS_SETTINGS";
+    field public static final String OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD = "android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD";
     field public static final String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG";
     field public static final String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT";
     field public static final String PACKET_KEEPALIVE_OFFLOAD = "android.permission.PACKET_KEEPALIVE_OFFLOAD";
@@ -1124,6 +1126,21 @@
     method public static boolean isChangeEnabled(long);
     method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, @NonNull String, @NonNull android.os.UserHandle);
     method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, int);
+    method @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) public static void setPackageOverride(@NonNull String, @NonNull java.util.Map<java.lang.Long,android.app.compat.PackageOverride>);
+  }
+
+  public final class PackageOverride {
+    method public long getMaxVersionCode();
+    method public long getMinVersionCode();
+    method public boolean isEnabled();
+  }
+
+  public static final class PackageOverride.Builder {
+    ctor public PackageOverride.Builder();
+    method @NonNull public android.app.compat.PackageOverride build();
+    method @NonNull public android.app.compat.PackageOverride.Builder setEnabled(boolean);
+    method @NonNull public android.app.compat.PackageOverride.Builder setMaxVersionCode(long);
+    method @NonNull public android.app.compat.PackageOverride.Builder setMinVersionCode(long);
   }
 
 }
@@ -1421,10 +1438,11 @@
 package android.apphibernation {
 
   public final class AppHibernationManager {
-    method public boolean isHibernatingForUser(@NonNull String);
-    method public boolean isHibernatingGlobally(@NonNull String);
-    method public void setHibernatingForUser(@NonNull String, boolean);
-    method public void setHibernatingGlobally(@NonNull String, boolean);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public java.util.List<java.lang.String> getHibernatingPackagesForUser();
+    method @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public boolean isHibernatingForUser(@NonNull String);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public boolean isHibernatingGlobally(@NonNull String);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public void setHibernatingForUser(@NonNull String, boolean);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public void setHibernatingGlobally(@NonNull String, boolean);
   }
 
 }
@@ -1480,6 +1498,7 @@
 
   public final class BluetoothDevice implements android.os.Parcelable {
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean cancelBondProcess();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean createBondOutOfBand(int, @Nullable android.bluetooth.OobData, @Nullable android.bluetooth.OobData);
     method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public byte[] getMetadata(int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getSimAccessPermission();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isConnected();
@@ -1530,7 +1549,6 @@
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnect(android.bluetooth.BluetoothDevice);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setPriority(android.bluetooth.BluetoothDevice, int);
   }
 
   public final class BluetoothHearingAid implements android.bluetooth.BluetoothProfile {
@@ -1656,6 +1674,55 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BufferConstraints> CREATOR;
   }
 
+  public final class OobData implements android.os.Parcelable {
+    method @NonNull public static android.bluetooth.OobData.ClassicBuilder createClassicBuilder(@NonNull byte[], @NonNull byte[], @NonNull byte[]);
+    method @NonNull public static android.bluetooth.OobData.LeBuilder createLeBuilder(@NonNull byte[], @NonNull byte[], int);
+    method @NonNull public byte[] getClassOfDevice();
+    method @NonNull public byte[] getClassicLength();
+    method @NonNull public byte[] getConfirmationHash();
+    method @NonNull public byte[] getDeviceAddressWithType();
+    method @Nullable public byte[] getDeviceName();
+    method @Nullable public byte[] getLeAppearance();
+    method @NonNull public int getLeDeviceRole();
+    method @NonNull public int getLeFlags();
+    method @Nullable public byte[] getLeTemporaryKey();
+    method @NonNull public byte[] getRandomizerHash();
+    field public static final int CLASS_OF_DEVICE_OCTETS = 3; // 0x3
+    field public static final int CONFIRMATION_OCTETS = 16; // 0x10
+    field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.OobData> CREATOR;
+    field public static final int DEVICE_ADDRESS_OCTETS = 7; // 0x7
+    field public static final int LE_APPEARANCE_OCTETS = 2; // 0x2
+    field public static final int LE_DEVICE_FLAG_OCTETS = 1; // 0x1
+    field public static final int LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL = 3; // 0x3
+    field public static final int LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL = 2; // 0x2
+    field public static final int LE_DEVICE_ROLE_CENTRAL_ONLY = 1; // 0x1
+    field public static final int LE_DEVICE_ROLE_OCTETS = 1; // 0x1
+    field public static final int LE_DEVICE_ROLE_PERIPHERAL_ONLY = 0; // 0x0
+    field public static final int LE_FLAG_BREDR_NOT_SUPPORTED = 2; // 0x2
+    field public static final int LE_FLAG_GENERAL_DISCOVERY_MODE = 1; // 0x1
+    field public static final int LE_FLAG_LIMITED_DISCOVERY_MODE = 0; // 0x0
+    field public static final int LE_FLAG_SIMULTANEOUS_CONTROLLER = 3; // 0x3
+    field public static final int LE_FLAG_SIMULTANEOUS_HOST = 4; // 0x4
+    field public static final int LE_TK_OCTETS = 16; // 0x10
+    field public static final int OOB_LENGTH_OCTETS = 2; // 0x2
+    field public static final int RANDOMIZER_OCTETS = 16; // 0x10
+  }
+
+  public static final class OobData.ClassicBuilder {
+    method @NonNull public android.bluetooth.OobData build();
+    method @NonNull public android.bluetooth.OobData.ClassicBuilder setClassOfDevice(@NonNull byte[]);
+    method @NonNull public android.bluetooth.OobData.ClassicBuilder setDeviceName(@NonNull byte[]);
+    method @NonNull public android.bluetooth.OobData.ClassicBuilder setRandomizerHash(@NonNull byte[]);
+  }
+
+  public static final class OobData.LeBuilder {
+    method @NonNull public android.bluetooth.OobData build();
+    method @NonNull public android.bluetooth.OobData.LeBuilder setDeviceName(@NonNull byte[]);
+    method @NonNull public android.bluetooth.OobData.LeBuilder setLeFlags(int);
+    method @NonNull public android.bluetooth.OobData.LeBuilder setLeTemporaryKey(@NonNull byte[]);
+    method @NonNull public android.bluetooth.OobData.LeBuilder setRandomizerHash(@NonNull byte[]);
+  }
+
 }
 
 package android.bluetooth.le {
@@ -1676,7 +1743,19 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.ResultStorageDescriptor> CREATOR;
   }
 
+  public final class ScanFilter implements android.os.Parcelable {
+    method public int getAddressType();
+    method @Nullable public byte[] getIrk();
+  }
+
+  public static final class ScanFilter.Builder {
+    method @NonNull public android.bluetooth.le.ScanFilter.Builder setDeviceAddress(@NonNull String, int);
+    method @NonNull public android.bluetooth.le.ScanFilter.Builder setDeviceAddress(@NonNull String, int, @NonNull byte[]);
+    field public static final int LEN_IRK_OCTETS = 16; // 0x10
+  }
+
   public final class ScanSettings implements android.os.Parcelable {
+    field public static final int SCAN_MODE_AMBIENT_DISCOVERY = 3; // 0x3
     field public static final int SCAN_RESULT_TYPE_ABBREVIATED = 1; // 0x1
     field public static final int SCAN_RESULT_TYPE_FULL = 0; // 0x0
   }
@@ -6373,22 +6452,22 @@
 
 package android.net.sip {
 
-  public class SipAudioCall {
-    method @Nullable public android.net.rtp.AudioGroup getAudioGroup();
-    method public void setAudioGroup(@NonNull android.net.rtp.AudioGroup);
+  @Deprecated public class SipAudioCall {
+    method @Deprecated @Nullable public android.net.rtp.AudioGroup getAudioGroup();
+    method @Deprecated public void setAudioGroup(@NonNull android.net.rtp.AudioGroup);
   }
 
-  public class SipManager {
-    method @NonNull public java.util.List<android.net.sip.SipProfile> getProfiles() throws android.net.sip.SipException;
-    field public static final String ACTION_SIP_CALL_OPTION_CHANGED = "android.net.sip.action.SIP_CALL_OPTION_CHANGED";
-    field public static final String ACTION_SIP_INCOMING_CALL = "android.net.sip.action.SIP_INCOMING_CALL";
-    field public static final String ACTION_SIP_REMOVE_PROFILE = "android.net.sip.action.SIP_REMOVE_PROFILE";
-    field public static final String ACTION_SIP_SERVICE_UP = "android.net.sip.action.SIP_SERVICE_UP";
-    field public static final String ACTION_START_SIP = "android.net.sip.action.START_SIP";
+  @Deprecated public class SipManager {
+    method @Deprecated @NonNull public java.util.List<android.net.sip.SipProfile> getProfiles() throws android.net.sip.SipException;
+    field @Deprecated public static final String ACTION_SIP_CALL_OPTION_CHANGED = "android.net.sip.action.SIP_CALL_OPTION_CHANGED";
+    field @Deprecated public static final String ACTION_SIP_INCOMING_CALL = "android.net.sip.action.SIP_INCOMING_CALL";
+    field @Deprecated public static final String ACTION_SIP_REMOVE_PROFILE = "android.net.sip.action.SIP_REMOVE_PROFILE";
+    field @Deprecated public static final String ACTION_SIP_SERVICE_UP = "android.net.sip.action.SIP_SERVICE_UP";
+    field @Deprecated public static final String ACTION_START_SIP = "android.net.sip.action.START_SIP";
   }
 
-  public class SipProfile implements java.lang.Cloneable android.os.Parcelable java.io.Serializable {
-    method public int getCallingUid();
+  @Deprecated public class SipProfile implements java.lang.Cloneable android.os.Parcelable java.io.Serializable {
+    method @Deprecated public int getCallingUid();
   }
 
 }
@@ -6409,12 +6488,12 @@
 package android.net.vcn {
 
   public class VcnManager {
-    method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void addVcnNetworkPolicyListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnNetworkPolicyListener);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void addVcnNetworkPolicyChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener);
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.vcn.VcnNetworkPolicyResult applyVcnNetworkPolicy(@NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties);
-    method public void removeVcnNetworkPolicyListener(@NonNull android.net.vcn.VcnManager.VcnNetworkPolicyListener);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void removeVcnNetworkPolicyChangeListener(@NonNull android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener);
   }
 
-  public static interface VcnManager.VcnNetworkPolicyListener {
+  public static interface VcnManager.VcnNetworkPolicyChangeListener {
     method public void onPolicyChanged();
   }
 
@@ -9356,6 +9435,10 @@
     field public static final String KEY_SUPPORT_CDMA_1X_VOICE_CALLS_BOOL = "support_cdma_1x_voice_calls_bool";
   }
 
+  public static final class CarrierConfigManager.Ims {
+    field public static final String KEY_PUBLISH_SERVICE_DESC_FEATURE_TAG_MAP_OVERRIDE_STRING_ARRAY = "ims.publish_service_desc_feature_tag_map_override_string_array";
+  }
+
   public static final class CarrierConfigManager.Wifi {
     field public static final String KEY_HOTSPOT_MAX_CLIENT_COUNT = "wifi.hotspot_maximum_client_count";
     field public static final String KEY_PREFIX = "wifi.";
@@ -9471,7 +9554,8 @@
 
   public final class DataSpecificRegistrationInfo implements android.os.Parcelable {
     method public int describeContents();
-    method @NonNull public android.telephony.LteVopsSupportInfo getLteVopsSupportInfo();
+    method @Deprecated @NonNull public android.telephony.LteVopsSupportInfo getLteVopsSupportInfo();
+    method @Nullable public android.telephony.VopsSupportInfo getVopsSupportInfo();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.DataSpecificRegistrationInfo> CREATOR;
   }
@@ -9503,14 +9587,29 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ImsiEncryptionInfo> CREATOR;
   }
 
-  public final class LteVopsSupportInfo implements android.os.Parcelable {
-    ctor public LteVopsSupportInfo(int, int);
+  public final class LinkCapacityEstimate implements android.os.Parcelable {
+    ctor public LinkCapacityEstimate(int, int, int);
     method public int describeContents();
+    method public int getDownlinkCapacityKbps();
+    method public int getType();
+    method public int getUplinkCapacityKbps();
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.LinkCapacityEstimate> CREATOR;
+    field public static final int INVALID = -1; // 0xffffffff
+    field public static final int LCE_TYPE_COMBINED = 2; // 0x2
+    field public static final int LCE_TYPE_PRIMARY = 0; // 0x0
+    field public static final int LCE_TYPE_SECONDARY = 1; // 0x1
+  }
+
+  public final class LteVopsSupportInfo extends android.telephony.VopsSupportInfo {
+    ctor public LteVopsSupportInfo(int, int);
     method public int getEmcBearerSupport();
     method public int getVopsSupport();
-    method public void writeToParcel(android.os.Parcel, int);
+    method public boolean isEmergencyServiceFallbackSupported();
+    method public boolean isEmergencyServiceSupported();
+    method public boolean isVopsSupported();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.LteVopsSupportInfo> CREATOR;
-    field public static final int LTE_STATUS_NOT_AVAILABLE = 1; // 0x1
+    field @Deprecated public static final int LTE_STATUS_NOT_AVAILABLE = 1; // 0x1
     field public static final int LTE_STATUS_NOT_SUPPORTED = 3; // 0x3
     field public static final int LTE_STATUS_SUPPORTED = 2; // 0x2
   }
@@ -9581,6 +9680,29 @@
     field public static final int RESULT_SUCCESS = 0; // 0x0
   }
 
+  public final class NrVopsSupportInfo extends android.telephony.VopsSupportInfo {
+    ctor public NrVopsSupportInfo(int, int, int);
+    method public int getEmcSupport();
+    method public int getEmfSupport();
+    method public int getVopsSupport();
+    method public boolean isEmergencyServiceFallbackSupported();
+    method public boolean isEmergencyServiceSupported();
+    method public boolean isVopsSupported();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.NrVopsSupportInfo> CREATOR;
+    field public static final int NR_STATUS_EMC_5GCN_ONLY = 1; // 0x1
+    field public static final int NR_STATUS_EMC_EUTRA_5GCN_ONLY = 2; // 0x2
+    field public static final int NR_STATUS_EMC_NOT_SUPPORTED = 0; // 0x0
+    field public static final int NR_STATUS_EMC_NR_EUTRA_5GCN = 3; // 0x3
+    field public static final int NR_STATUS_EMF_5GCN_ONLY = 1; // 0x1
+    field public static final int NR_STATUS_EMF_EUTRA_5GCN_ONLY = 2; // 0x2
+    field public static final int NR_STATUS_EMF_NOT_SUPPORTED = 0; // 0x0
+    field public static final int NR_STATUS_EMF_NR_EUTRA_5GCN = 3; // 0x3
+    field public static final int NR_STATUS_VOPS_3GPP_SUPPORTED = 1; // 0x1
+    field public static final int NR_STATUS_VOPS_NON_3GPP_SUPPORTED = 2; // 0x2
+    field public static final int NR_STATUS_VOPS_NOT_SUPPORTED = 0; // 0x0
+  }
+
   public interface NumberVerificationCallback {
     method public default void onCallReceived(@NonNull String);
     method public default void onVerificationFailed(int);
@@ -9979,6 +10101,8 @@
     field public static final int EVENT_DISPLAY_INFO_CHANGED = 21; // 0x15
     field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; // 0x19
     field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; // 0x1c
+    field @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_LEGACY_CALL_STATE_CHANGED = 36; // 0x24
+    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_LINK_CAPACITY_ESTIMATE_CHANGED = 37; // 0x25
     field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3; // 0x3
     field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_OEM_HOOK_RAW = 15; // 0xf
     field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29; // 0x1d
@@ -9998,7 +10122,7 @@
   }
 
   public static interface TelephonyCallback.AllowedNetworkTypesListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onAllowedNetworkTypesChanged(@NonNull java.util.Map<java.lang.Integer,java.lang.Long>);
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onAllowedNetworkTypesChanged(int, long);
   }
 
   public static interface TelephonyCallback.CallAttributesListener {
@@ -10009,6 +10133,10 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onDataEnabledChanged(boolean, int);
   }
 
+  public static interface TelephonyCallback.LinkCapacityEstimateChangedListener {
+    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onLinkCapacityEstimateChanged(@NonNull java.util.List<android.telephony.LinkCapacityEstimate>);
+  }
+
   public static interface TelephonyCallback.OutgoingEmergencyCallListener {
     method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
   }
@@ -10021,10 +10149,6 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
   }
 
-  public static interface TelephonyCallback.PhysicalChannelConfigListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPhysicalChannelConfigChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
-  }
-
   public static interface TelephonyCallback.PreciseCallStateListener {
     method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
   }
@@ -10080,7 +10204,6 @@
     method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallForwarding(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CallForwardingInfoCallback);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallWaitingStatus(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
-    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierBandwidth getCarrierBandwidth();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int);
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
@@ -10139,7 +10262,7 @@
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int);
-    method public boolean isNrDualConnectivityEnabled();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isNrDualConnectivityEnabled();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
@@ -10179,7 +10302,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult setIccLockEnabled(boolean, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabledStatus(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
-    method public int setNrDualConnectivityState(int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setNrDualConnectivityState(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadio(boolean);
@@ -10216,6 +10339,7 @@
     field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
     field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
     field public static final int ALLOWED_NETWORK_TYPES_REASON_CARRIER = 2; // 0x2
+    field public static final int ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G = 3; // 0x3
     field public static final int ALLOWED_NETWORK_TYPES_REASON_POWER = 1; // 0x1
     field public static final int ALLOWED_NETWORK_TYPES_REASON_USER = 0; // 0x0
     field public static final int CALL_WAITING_STATUS_DISABLED = 2; // 0x2
@@ -10223,7 +10347,9 @@
     field public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; // 0x4
     field public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; // 0x3
     field public static final String CAPABILITY_ALLOWED_NETWORK_TYPES_USED = "CAPABILITY_ALLOWED_NETWORK_TYPES_USED";
+    field public static final String CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE = "CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE";
     field public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE = "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE";
+    field public static final String CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING = "CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING";
     field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
     field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
     field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
@@ -10377,6 +10503,16 @@
     method public static final void setSmsFilterSettings(android.content.Context, android.telecom.PhoneAccountHandle, android.telephony.VisualVoicemailSmsFilterSettings);
   }
 
+  public abstract class VopsSupportInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public abstract boolean equals(Object);
+    method public abstract int hashCode();
+    method public abstract boolean isEmergencyServiceFallbackSupported();
+    method public abstract boolean isEmergencyServiceSupported();
+    method public abstract boolean isVopsSupported();
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.VopsSupportInfo> CREATOR;
+  }
+
 }
 
 package android.telephony.cdma {
@@ -10565,18 +10701,31 @@
   }
 
   public final class EpsBearerQosSessionAttributes implements android.os.Parcelable android.net.QosSessionAttributes {
-    method @NonNull public static android.telephony.data.EpsBearerQosSessionAttributes create(@NonNull android.os.Parcel);
     method public int describeContents();
-    method public long getGuaranteedDownlinkBitRate();
-    method public long getGuaranteedUplinkBitRate();
-    method public long getMaxDownlinkBitRate();
-    method public long getMaxUplinkBitRate();
-    method public int getQci();
+    method public long getGuaranteedDownlinkBitRateKbps();
+    method public long getGuaranteedUplinkBitRateKbps();
+    method public long getMaxDownlinkBitRateKbps();
+    method public long getMaxUplinkBitRateKbps();
+    method public int getQosIdentifier();
     method @NonNull public java.util.List<java.net.InetSocketAddress> getRemoteAddresses();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.EpsBearerQosSessionAttributes> CREATOR;
   }
 
+  public final class NrQosSessionAttributes implements android.os.Parcelable android.net.QosSessionAttributes {
+    method public int describeContents();
+    method @NonNull public java.time.Duration getBitRateWindowDuration();
+    method public long getGuaranteedDownlinkBitRateKbps();
+    method public long getGuaranteedUplinkBitRateKbps();
+    method public long getMaxDownlinkBitRateKbps();
+    method public long getMaxUplinkBitRateKbps();
+    method @IntRange(from=1, to=63) public int getQosFlowIdentifier();
+    method public int getQosIdentifier();
+    method @NonNull public java.util.List<java.net.InetSocketAddress> getRemoteAddresses();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.NrQosSessionAttributes> CREATOR;
+  }
+
   public abstract class QualifiedNetworksService extends android.app.Service {
     ctor public QualifiedNetworksService();
     method @NonNull public abstract android.telephony.data.QualifiedNetworksService.NetworkAvailabilityProvider onCreateNetworkAvailabilityProvider(int);
@@ -11543,6 +11692,7 @@
     method public void onAutoConfigurationErrorReceived(int, @NonNull String);
     method public void onConfigurationChanged(@NonNull byte[]);
     method public void onConfigurationReset();
+    method public void onPreProvisioningReceived(@NonNull byte[]);
     method public void onRemoved();
   }
 
@@ -11567,7 +11717,7 @@
     method @NonNull public String getServiceId();
     method @NonNull public String getServiceVersion();
     method @NonNull public String getStatus();
-    method @Nullable public String getTimestamp();
+    method @Nullable public java.time.Instant getTime();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactPresenceTuple> CREATOR;
     field public static final String SERVICE_ID_CALL_COMPOSER = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callcomposer";
@@ -11594,7 +11744,7 @@
     method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setContactUri(@NonNull android.net.Uri);
     method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setServiceCapabilities(@NonNull android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities);
     method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setServiceDescription(@NonNull String);
-    method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setTimestamp(@NonNull String);
+    method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setTime(@NonNull java.time.Instant);
   }
 
   public static final class RcsContactPresenceTuple.ServiceCapabilities implements android.os.Parcelable {
@@ -11624,6 +11774,7 @@
     method @Nullable public android.telephony.ims.RcsContactPresenceTuple getCapabilityTuple(@NonNull String);
     method @NonNull public java.util.List<android.telephony.ims.RcsContactPresenceTuple> getCapabilityTuples();
     method @NonNull public android.net.Uri getContactUri();
+    method @NonNull public java.util.Set<java.lang.String> getFeatureTags();
     method public int getRequestResult();
     method public int getSourceType();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -11638,6 +11789,14 @@
     field public static final int SOURCE_TYPE_NETWORK = 0; // 0x0
   }
 
+  public static final class RcsContactUceCapability.OptionsBuilder {
+    ctor public RcsContactUceCapability.OptionsBuilder(@NonNull android.net.Uri);
+    method @NonNull public android.telephony.ims.RcsContactUceCapability.OptionsBuilder addFeatureTag(@NonNull String);
+    method @NonNull public android.telephony.ims.RcsContactUceCapability.OptionsBuilder addFeatureTags(@NonNull java.util.Set<java.lang.String>);
+    method @NonNull public android.telephony.ims.RcsContactUceCapability build();
+    method @NonNull public android.telephony.ims.RcsContactUceCapability.OptionsBuilder setRequestResult(int);
+  }
+
   public static final class RcsContactUceCapability.PresenceBuilder {
     ctor public RcsContactUceCapability.PresenceBuilder(@NonNull android.net.Uri, int, int);
     method @NonNull public android.telephony.ims.RcsContactUceCapability.PresenceBuilder addCapabilityTuple(@NonNull android.telephony.ims.RcsContactPresenceTuple);
@@ -11650,7 +11809,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getUcePublishState() throws android.telephony.ims.ImsException;
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeOnPublishStateChangedListener(@NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException;
     method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, android.Manifest.permission.READ_CONTACTS}) public void requestAvailability(@NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
-    method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, android.Manifest.permission.READ_CONTACTS}) public void requestCapabilities(@NonNull java.util.List<android.net.Uri>, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
+    method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, android.Manifest.permission.READ_CONTACTS}) public void requestCapabilities(@NonNull java.util.Collection<android.net.Uri>, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
     field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
     field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; // 0x1
@@ -11931,7 +12090,7 @@
 package android.telephony.ims.stub {
 
   public interface CapabilityExchangeEventListener {
-    method public void onRemoteCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.List<java.lang.String>, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener.OptionsRequestCallback) throws android.telephony.ims.ImsException;
+    method public void onRemoteCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.Set<java.lang.String>, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener.OptionsRequestCallback) throws android.telephony.ims.ImsException;
     method public void onRequestPublishCapabilities(int) throws android.telephony.ims.ImsException;
     method public void onUnpublish() throws android.telephony.ims.ImsException;
   }
@@ -12012,6 +12171,7 @@
     method public int getConfigInt(int);
     method public String getConfigString(int);
     method public final void notifyAutoConfigurationErrorReceived(int, @NonNull String);
+    method public final void notifyPreProvisioningReceived(@NonNull byte[]);
     method public final void notifyProvisionedValueChanged(int, int);
     method public final void notifyProvisionedValueChanged(int, String);
     method public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
@@ -12126,8 +12286,8 @@
   public class RcsCapabilityExchangeImplBase {
     ctor public RcsCapabilityExchangeImplBase(@NonNull java.util.concurrent.Executor);
     method public void publishCapabilities(@NonNull String, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback);
-    method public void sendOptionsCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.List<java.lang.String>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.OptionsResponseCallback);
-    method public void subscribeForCapabilities(@NonNull java.util.List<android.net.Uri>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback);
+    method public void sendOptionsCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.Set<java.lang.String>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.OptionsResponseCallback);
+    method public void subscribeForCapabilities(@NonNull java.util.Collection<android.net.Uri>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback);
     field public static final int COMMAND_CODE_FETCH_ERROR = 3; // 0x3
     field public static final int COMMAND_CODE_GENERIC_FAILURE = 1; // 0x1
     field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 5; // 0x5
@@ -12295,6 +12455,7 @@
 package android.uwb {
 
   public final class AngleMeasurement implements android.os.Parcelable {
+    ctor public AngleMeasurement(@FloatRange(from=-3.141592653589793, to=3.141592653589793) double, @FloatRange(from=0.0, to=3.141592653589793) double, @FloatRange(from=0.0, to=1.0) double);
     method public int describeContents();
     method @FloatRange(from=0.0, to=1.0) public double getConfidenceLevel();
     method @FloatRange(from=0.0, to=3.141592653589793) public double getErrorRadians();
@@ -12303,14 +12464,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.uwb.AngleMeasurement> CREATOR;
   }
 
-  public static final class AngleMeasurement.Builder {
-    ctor public AngleMeasurement.Builder();
-    method @NonNull public android.uwb.AngleMeasurement build();
-    method @NonNull public android.uwb.AngleMeasurement.Builder setConfidenceLevel(double);
-    method @NonNull public android.uwb.AngleMeasurement.Builder setErrorRadians(double);
-    method @NonNull public android.uwb.AngleMeasurement.Builder setRadians(double);
-  }
-
   public final class AngleOfArrivalMeasurement implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public android.uwb.AngleMeasurement getAltitude();
@@ -12320,10 +12473,9 @@
   }
 
   public static final class AngleOfArrivalMeasurement.Builder {
-    ctor public AngleOfArrivalMeasurement.Builder();
+    ctor public AngleOfArrivalMeasurement.Builder(@NonNull android.uwb.AngleMeasurement);
     method @NonNull public android.uwb.AngleOfArrivalMeasurement build();
     method @NonNull public android.uwb.AngleOfArrivalMeasurement.Builder setAltitude(@NonNull android.uwb.AngleMeasurement);
-    method @NonNull public android.uwb.AngleOfArrivalMeasurement.Builder setAzimuth(@NonNull android.uwb.AngleMeasurement);
   }
 
   public final class DistanceMeasurement implements android.os.Parcelable {
@@ -12423,7 +12575,7 @@
   public final class UwbManager {
     method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public long elapsedRealtimeResolutionNanos();
     method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public android.os.PersistableBundle getSpecificationInfo();
-    method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public AutoCloseable openRangingSession(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.RangingSession.Callback);
+    method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public android.os.CancellationSignal openRangingSession(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.RangingSession.Callback);
     method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void registerAdapterStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.uwb.UwbManager.AdapterStateCallback);
     method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void unregisterAdapterStateCallback(@NonNull android.uwb.UwbManager.AdapterStateCallback);
   }
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index 0c02c43..8895494 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -48,6 +48,14 @@
 
 }
 
+package android.bluetooth {
+
+  public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
+    method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setPriority(android.bluetooth.BluetoothDevice, int);
+  }
+
+}
+
 package android.content {
 
   public class Intent implements java.lang.Cloneable android.os.Parcelable {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index b0b4556..1d094c3 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -996,6 +996,8 @@
 
   public class NetworkPolicyManager {
     method public boolean getRestrictBackground();
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidNetworkingBlocked(int, boolean);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidRestrictedOnMeteredNetworks(int);
     method @NonNull public static String resolveNetworkId(@NonNull android.net.wifi.WifiConfiguration);
     method public void setRestrictBackground(boolean);
   }
@@ -1604,6 +1606,10 @@
     method @NonNull public android.telecom.ConnectionRequest.Builder setVideoState(int);
   }
 
+  public abstract class ConnectionService extends android.app.Service {
+    method public void onBindClient(@Nullable android.content.Intent);
+  }
+
 }
 
 package android.telephony {
@@ -1646,6 +1652,7 @@
   public class ServiceState implements android.os.Parcelable {
     method public void addNetworkRegistrationInfo(android.telephony.NetworkRegistrationInfo);
     method public int getDataNetworkType();
+    method public int getDataRegState();
     method public void setCdmaSystemAndNetworkId(int, int);
     method public void setCellBandwidths(int[]);
     method public void setChannelNumber(int);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b99d5cd..65f2c02 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2770,7 +2770,7 @@
                         memInfo.getTotalPrivateDirty(),
                         memInfo.getTotalPrivateClean(),
                         memInfo.hasSwappedOutPss ? memInfo.getTotalSwappedOutPss() :
-                        memInfo.getTotalSwappedOut(), memInfo.getTotalPss(),
+                        memInfo.getTotalSwappedOut(), memInfo.getTotalRss(),
                         nativeMax+dalvikMax,
                         nativeAllocated+dalvikAllocated, nativeFree+dalvikFree);
             }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 331a0b1..267d029 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -117,11 +117,13 @@
 import android.net.IEthernetManager;
 import android.net.IIpSecService;
 import android.net.INetworkPolicyManager;
+import android.net.IPacProxyManager;
 import android.net.IVpnManager;
 import android.net.IpSecManager;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkScoreManager;
 import android.net.NetworkWatchlistManager;
+import android.net.PacProxyManager;
 import android.net.TetheringManager;
 import android.net.VpnManager;
 import android.net.lowpan.ILowpanManager;
@@ -346,6 +348,15 @@
         // (which extends it).
         SYSTEM_SERVICE_NAMES.put(android.text.ClipboardManager.class, Context.CLIPBOARD_SERVICE);
 
+        registerService(Context.PAC_PROXY_SERVICE, PacProxyManager.class,
+                new CachedServiceFetcher<PacProxyManager>() {
+            @Override
+            public PacProxyManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+                IBinder b = ServiceManager.getServiceOrThrow(Context.PAC_PROXY_SERVICE);
+                IPacProxyManager service = IPacProxyManager.Stub.asInterface(b);
+                return new PacProxyManager(ctx.getOuterContext(), service);
+            }});
+
         registerService(Context.NETD_SERVICE, IBinder.class, new StaticServiceFetcher<IBinder>() {
             @Override
             public IBinder createService() throws ServiceNotFoundException {
diff --git a/core/java/android/app/compat/CompatChanges.java b/core/java/android/app/compat/CompatChanges.java
index ab38832..74e1ece 100644
--- a/core/java/android/app/compat/CompatChanges.java
+++ b/core/java/android/app/compat/CompatChanges.java
@@ -104,16 +104,15 @@
      *
      * @param packageName The package name of the app in question.
      * @param overrides A map from changeId to the override applied for this change id.
-     * @hide
      */
-    @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG)
-    public static void setPackageOverride(String packageName,
-            Map<Long, PackageOverride> overrides) {
+    @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
+    public static void setPackageOverride(@NonNull String packageName,
+            @NonNull Map<Long, PackageOverride> overrides) {
         IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
                 ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
         CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(overrides);
         try {
-            platformCompat.setOverridesFromInstaller(config, packageName);
+            platformCompat.setOverridesOnReleaseBuilds(config, packageName);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/compat/PackageOverride.java b/core/java/android/app/compat/PackageOverride.java
index 9f97cd4..59b3555 100644
--- a/core/java/android/app/compat/PackageOverride.java
+++ b/core/java/android/app/compat/PackageOverride.java
@@ -17,8 +17,9 @@
 package android.app.compat;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
-import android.os.Parcelable;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -32,15 +33,16 @@
  *
  * @hide
  */
-public class PackageOverride implements Parcelable {
+@SystemApi
+public final class PackageOverride {
 
+    /** @hide */
     @IntDef({
             VALUE_UNDEFINED,
             VALUE_ENABLED,
             VALUE_DISABLED
     })
     @Retention(RetentionPolicy.SOURCE)
-    /** @hide */
     public @interface EvaluatedOverride {
     }
 
@@ -75,10 +77,6 @@
         this.mEnabled = enabled;
     }
 
-    private PackageOverride(Parcel in) {
-        this(in.readLong(), in.readLong(), in.readBoolean());
-    }
-
     /**
      * Evaluate the override for the given {@code versionCode}. If no override is defined for
      * the specified version code, {@link #VALUE_UNDEFINED} is returned.
@@ -114,25 +112,23 @@
     }
 
     /** Returns the enabled value for the override. */
-    public boolean getEnabled() {
+    public boolean isEnabled() {
         return mEnabled;
     }
 
     /** @hide */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /** @hide */
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(Parcel dest) {
         dest.writeLong(mMinVersionCode);
         dest.writeLong(mMaxVersionCode);
         dest.writeBoolean(mEnabled);
     }
 
     /** @hide */
+    public static PackageOverride createFromParcel(Parcel in) {
+        return new PackageOverride(in.readLong(), in.readLong(), in.readBoolean());
+    }
+
+    /** @hide */
     @Override
     public String toString() {
         if (mMinVersionCode == Long.MIN_VALUE && mMaxVersionCode == Long.MAX_VALUE) {
@@ -141,25 +137,10 @@
         return String.format("[%d,%d,%b]", mMinVersionCode, mMaxVersionCode, mEnabled);
     }
 
-    /** @hide */
-    public static final Creator<PackageOverride> CREATOR =
-            new Creator<PackageOverride>() {
-
-                @Override
-                public PackageOverride createFromParcel(Parcel in) {
-                    return new PackageOverride(in);
-                }
-
-                @Override
-                public PackageOverride[] newArray(int size) {
-                    return new PackageOverride[size];
-                }
-            };
-
     /**
      * Builder to construct a PackageOverride.
      */
-    public static class Builder {
+    public static final class Builder {
         private long mMinVersionCode = Long.MIN_VALUE;
         private long mMaxVersionCode = Long.MAX_VALUE;
         private boolean mEnabled;
@@ -169,6 +150,7 @@
          *
          * default value: {@code Long.MIN_VALUE}.
          */
+        @NonNull
         public Builder setMinVersionCode(long minVersionCode) {
             mMinVersionCode = minVersionCode;
             return this;
@@ -179,6 +161,7 @@
          *
          * default value: {@code Long.MAX_VALUE}.
          */
+        @NonNull
         public Builder setMaxVersionCode(long maxVersionCode) {
             mMaxVersionCode = maxVersionCode;
             return this;
@@ -189,6 +172,7 @@
          *
          * default value: {@code false}.
          */
+        @NonNull
         public Builder setEnabled(boolean enabled) {
             mEnabled = enabled;
             return this;
@@ -200,6 +184,7 @@
          * @throws IllegalArgumentException if {@code minVersionCode} is larger than
          *                                  {@code maxVersionCode}.
          */
+        @NonNull
         public PackageOverride build() {
             if (mMinVersionCode > mMaxVersionCode) {
                 throw new IllegalArgumentException("minVersionCode must not be larger than "
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 098d8b6..9f1132b 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -24,6 +24,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
+import android.annotation.WorkerThread;
 import android.app.usage.NetworkStats.Bucket;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -201,6 +202,7 @@
      * default network {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
      * metered {@link NetworkStats.Bucket#METERED_ALL},
      * and roaming {@link NetworkStats.Bucket#ROAMING_ALL}.
+     * This may take a long time, and apps should avoid calling this on their main thread.
      *
      * @param networkType As defined in {@link ConnectivityManager}, e.g.
      *            {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -219,6 +221,7 @@
      * @return Bucket object or null if permissions are insufficient or error happened during
      *         statistics collection.
      */
+    @WorkerThread
     public Bucket querySummaryForDevice(int networkType, String subscriberId,
             long startTime, long endTime) throws SecurityException, RemoteException {
         NetworkTemplate template;
@@ -240,6 +243,7 @@
      * uid {@link NetworkStats.Bucket#UID_ALL}, tag {@link NetworkStats.Bucket#TAG_NONE},
      * metered {@link NetworkStats.Bucket#METERED_ALL}, and roaming
      * {@link NetworkStats.Bucket#ROAMING_ALL}.
+     * This may take a long time, and apps should avoid calling this on their main thread.
      *
      * @param networkType As defined in {@link ConnectivityManager}, e.g.
      *            {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -258,6 +262,7 @@
      * @return Bucket object or null if permissions are insufficient or error happened during
      *         statistics collection.
      */
+    @WorkerThread
     public Bucket querySummaryForUser(int networkType, String subscriberId, long startTime,
             long endTime) throws SecurityException, RemoteException {
         NetworkTemplate template;
@@ -283,6 +288,7 @@
      * means buckets' start and end timestamps are going to be the same as the 'startTime' and
      * 'endTime' parameters. State, uid, metered, and roaming are going to vary, and tag is going to
      * be the same.
+     * This may take a long time, and apps should avoid calling this on their main thread.
      *
      * @param networkType As defined in {@link ConnectivityManager}, e.g.
      *            {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -301,6 +307,7 @@
      * @return Statistics object or null if permissions are insufficient or error happened during
      *         statistics collection.
      */
+    @WorkerThread
     public NetworkStats querySummary(int networkType, String subscriberId, long startTime,
             long endTime) throws SecurityException, RemoteException {
         NetworkTemplate template;
@@ -326,9 +333,11 @@
 
     /**
      * Query network usage statistics details for a given uid.
+     * This may take a long time, and apps should avoid calling this on their main thread.
      *
      * @see #queryDetailsForUidTagState(int, String, long, long, int, int, int)
      */
+    @WorkerThread
     public NetworkStats queryDetailsForUid(int networkType, String subscriberId,
             long startTime, long endTime, int uid) throws SecurityException {
         return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid,
@@ -344,9 +353,11 @@
 
     /**
      * Query network usage statistics details for a given uid and tag.
+     * This may take a long time, and apps should avoid calling this on their main thread.
      *
      * @see #queryDetailsForUidTagState(int, String, long, long, int, int, int)
      */
+    @WorkerThread
     public NetworkStats queryDetailsForUidTag(int networkType, String subscriberId,
             long startTime, long endTime, int uid, int tag) throws SecurityException {
         return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid,
@@ -365,6 +376,7 @@
      * <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
      * interpolate across partial buckets. Since bucket length is in the order of hours, this
      * method cannot be used to measure data usage on a fine grained time scale.
+     * This may take a long time, and apps should avoid calling this on their main thread.
      *
      * @param networkType As defined in {@link ConnectivityManager}, e.g.
      *            {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -387,6 +399,7 @@
      * @return Statistics object or null if an error happened during statistics collection.
      * @throws SecurityException if permissions are insufficient to read network statistics.
      */
+    @WorkerThread
     public NetworkStats queryDetailsForUidTagState(int networkType, String subscriberId,
             long startTime, long endTime, int uid, int tag, int state) throws SecurityException {
         NetworkTemplate template;
@@ -425,6 +438,7 @@
      * <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
      * interpolate across partial buckets. Since bucket length is in the order of hours, this
      * method cannot be used to measure data usage on a fine grained time scale.
+     * This may take a long time, and apps should avoid calling this on their main thread.
      *
      * @param networkType As defined in {@link ConnectivityManager}, e.g.
      *            {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -443,6 +457,7 @@
      * @return Statistics object or null if permissions are insufficient or error happened during
      *         statistics collection.
      */
+    @WorkerThread
     public NetworkStats queryDetails(int networkType, String subscriberId, long startTime,
             long endTime) throws SecurityException, RemoteException {
         NetworkTemplate template;
diff --git a/core/java/android/apphibernation/AppHibernationManager.java b/core/java/android/apphibernation/AppHibernationManager.java
index 7281d50..de77848 100644
--- a/core/java/android/apphibernation/AppHibernationManager.java
+++ b/core/java/android/apphibernation/AppHibernationManager.java
@@ -17,12 +17,15 @@
 package android.apphibernation;
 
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 
+import java.util.List;
+
 /**
  * This class provides an API surface for system apps to manipulate the app hibernation
  * state of a package for the user provided in the context.
@@ -54,6 +57,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(value = android.Manifest.permission.MANAGE_APP_HIBERNATION)
     public boolean isHibernatingForUser(@NonNull String packageName) {
         try {
             return mIAppHibernationService.isHibernatingForUser(packageName, mContext.getUserId());
@@ -68,6 +72,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(value = android.Manifest.permission.MANAGE_APP_HIBERNATION)
     public void setHibernatingForUser(@NonNull String packageName, boolean isHibernating) {
         try {
             mIAppHibernationService.setHibernatingForUser(packageName, mContext.getUserId(),
@@ -83,6 +88,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(value = android.Manifest.permission.MANAGE_APP_HIBERNATION)
     public boolean isHibernatingGlobally(@NonNull String packageName) {
         try {
             return mIAppHibernationService.isHibernatingGlobally(packageName);
@@ -99,6 +105,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(value = android.Manifest.permission.MANAGE_APP_HIBERNATION)
     public void setHibernatingGlobally(@NonNull String packageName, boolean isHibernating) {
         try {
             mIAppHibernationService.setHibernatingGlobally(packageName, isHibernating);
@@ -106,4 +113,20 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Get the hibernating packages for the user. This is equivalent to the list of packages for
+     * the user that return true for {@link #isHibernatingForUser}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(value = android.Manifest.permission.MANAGE_APP_HIBERNATION)
+    public @NonNull List<String> getHibernatingPackagesForUser() {
+        try {
+            return mIAppHibernationService.getHibernatingPackagesForUser(mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/apphibernation/IAppHibernationService.aidl b/core/java/android/apphibernation/IAppHibernationService.aidl
index 6a068ee..afdb3fe 100644
--- a/core/java/android/apphibernation/IAppHibernationService.aidl
+++ b/core/java/android/apphibernation/IAppHibernationService.aidl
@@ -25,4 +25,5 @@
     void setHibernatingForUser(String packageName, int userId, boolean isHibernating);
     boolean isHibernatingGlobally(String packageName);
     void setHibernatingGlobally(String packageName, boolean isHibernating);
+    List<String> getHibernatingPackagesForUser(int userId);
 }
\ No newline at end of file
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index cc0b22a..38863c2 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -52,6 +52,8 @@
 import android.util.Log;
 import android.util.Pair;
 
+import com.android.internal.util.Preconditions;
+
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -3118,6 +3120,25 @@
         return true;
     }
 
+    /**
+     * Determines whether a String Bluetooth address, such as "00:43:A8:23:10:F0"
+     * is a RANDOM STATIC address.
+     *
+     * RANDOM STATIC: (addr & 0b11) == 0b11
+     * RANDOM RESOLVABLE: (addr & 0b11) == 0b10
+     * RANDOM non-RESOLVABLE: (addr & 0b11) == 0b00
+     *
+     * @param address Bluetooth address as string
+     * @return true if the 2 Least Significant Bits of the address equals 0b11.
+     *
+     * @hide
+     */
+    public static boolean isAddressRandomStatic(@NonNull String address) {
+        Preconditions.checkNotNull(address);
+        return checkBluetoothAddress(address)
+                && (Integer.parseInt(address.split(":")[5], 16) & 0b11) == 0b11;
+    }
+
     @UnsupportedAppUsage
     /*package*/ IBluetoothManager getBluetoothManager() {
         return mManagerService;
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 89030bc..c30b8af 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1002,6 +1002,24 @@
     public static final String EXTRA_MAS_INSTANCE =
             "android.bluetooth.device.extra.MAS_INSTANCE";
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+        prefix = { "ADDRESS_TYPE_" },
+        value = {
+            /** Hardware MAC Address */
+            ADDRESS_TYPE_PUBLIC,
+            /** Address is either resolvable, non-resolvable or static.*/
+            ADDRESS_TYPE_RANDOM,
+        }
+    )
+    public @interface AddressType {}
+
+    /** Hardware MAC Address of the device */
+    public static final int ADDRESS_TYPE_PUBLIC = 0;
+    /** Address is either resolvable, non-resolvable or static. */
+    public static final int ADDRESS_TYPE_RANDOM = 1;
+
     /**
      * Lazy initialization. Guaranteed final after first object constructed, or
      * getService() called.
@@ -1010,6 +1028,7 @@
     private static volatile IBluetooth sService;
 
     private final String mAddress;
+    @AddressType private final int mAddressType;
 
     /*package*/
     @UnsupportedAppUsage
@@ -1064,6 +1083,7 @@
         }
 
         mAddress = address;
+        mAddressType = ADDRESS_TYPE_PUBLIC;
     }
 
     @Override
@@ -1279,7 +1299,6 @@
      * the bonding process completes, and its result.
      * <p>Android system services will handle the necessary user interactions
      * to confirm and complete the bonding process.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
      *
      * @param transport The transport to use for the pairing procedure.
      * @return false on immediate error, true if bonding will begin
@@ -1287,8 +1306,9 @@
      * @hide
      */
     @UnsupportedAppUsage
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public boolean createBond(int transport) {
-        return createBondOutOfBand(transport, null);
+        return createBondInternal(transport, null, null);
     }
 
     /**
@@ -1302,21 +1322,38 @@
      * <p>Android system services will handle the necessary user interactions
      * to confirm and complete the bonding process.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
+     * <p>There are two possible versions of OOB Data.  This data can come in as
+     * P192 or P256.  This is a reference to the cryptography used to generate the key.
+     * The caller may pass one or both.  If both types of data are passed, then the
+     * P256 data will be preferred, and thus used.
      *
      * @param transport - Transport to use
-     * @param oobData - Out Of Band data
+     * @param remoteP192Data - Out Of Band data (P192) or null
+     * @param remoteP256Data - Out Of Band data (P256) or null
      * @return false on immediate error, true if bonding will begin
      * @hide
      */
-    public boolean createBondOutOfBand(int transport, OobData oobData) {
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public boolean createBondOutOfBand(int transport, @Nullable OobData remoteP192Data,
+            @Nullable OobData remoteP256Data) {
+        if (remoteP192Data == null && remoteP256Data == null) {
+            throw new IllegalArgumentException(
+                "One or both arguments for the OOB data types are required to not be null."
+                + "  Please use createBond() instead if you do not have OOB data to pass.");
+        }
+        return createBondInternal(transport, remoteP192Data, remoteP256Data);
+    }
+
+    private boolean createBondInternal(int transport, @Nullable OobData remoteP192Data,
+            @Nullable OobData remoteP256Data) {
         final IBluetooth service = sService;
         if (service == null) {
             Log.w(TAG, "BT not enabled, createBondOutOfBand failed");
             return false;
         }
         try {
-            return service.createBond(this, transport, oobData);
+            return service.createBond(this, transport, remoteP192Data, remoteP256Data);
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         }
@@ -1347,27 +1384,6 @@
     }
 
     /**
-     * Set the Out Of Band data for a remote device to be used later
-     * in the pairing mechanism. Users can obtain this data through other
-     * trusted channels
-     *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
-     *
-     * @param hash Simple Secure pairing hash
-     * @param randomizer The random key obtained using OOB
-     * @return false on error; true otherwise
-     * @hide
-     */
-    public boolean setDeviceOutOfBandData(byte[] hash, byte[] randomizer) {
-        //TODO(BT)
-      /*
-      try {
-        return sService.setDeviceOutOfBandData(this, hash, randomizer);
-      } catch (RemoteException e) {Log.e(TAG, "", e);} */
-        return false;
-    }
-
-    /**
      * Cancel an in-progress bonding request started with {@link #createBond}.
      *
      * @return true on success, false on error
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 4fb5577..632572d 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -567,6 +567,7 @@
      * @return true if priority is set, false on error
      * @hide
      * @deprecated Replaced with {@link #setConnectionPolicy(BluetoothDevice, int)}
+     * @removed
      */
     @Deprecated
     @SystemApi
diff --git a/core/java/android/bluetooth/OobData.java b/core/java/android/bluetooth/OobData.java
index 0d0c6ab..08d694e 100644
--- a/core/java/android/bluetooth/OobData.java
+++ b/core/java/android/bluetooth/OobData.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,88 +16,949 @@
 
 package android.bluetooth;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.util.Preconditions;
+
+import java.lang.IllegalArgumentException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Out Of Band Data for Bluetooth device pairing.
  *
  * <p>This object represents optional data obtained from a remote device through
- * an out-of-band channel (eg. NFC).
+ * an out-of-band channel (eg. NFC, QR).
+ *
+ * <p>References:
+ * NFC AD Forum SSP 1.1 (AD)
+ * {@link https://members.nfc-forum.org//apps/group_public/download.php/24620/NFCForum-AD-BTSSP_1_1.pdf}
+ * Core Specification Supplement (CSS) V9
+ *
+ * <p>There are several BR/EDR Examples
+ *
+ * <p>Negotiated Handover:
+ *   Bluetooth Carrier Configuration Record:
+ *    - OOB Data Length
+ *    - Device Address
+ *    - Class of Device
+ *    - Simple Pairing Hash C
+ *    - Simple Pairing Randomizer R
+ *    - Service Class UUID
+ *    - Bluetooth Local Name
+ *
+ * <p>Static Handover:
+ *   Bluetooth Carrier Configuration Record:
+ *    - OOB Data Length
+ *    - Device Address
+ *    - Class of Device
+ *    - Service Class UUID
+ *    - Bluetooth Local Name
+ *
+ * <p>Simplified Tag Format for Single BT Carrier:
+ *   Bluetooth OOB Data Record:
+ *    - OOB Data Length
+ *    - Device Address
+ *    - Class of Device
+ *    - Service Class UUID
+ *    - Bluetooth Local Name
  *
  * @hide
  */
-public class OobData implements Parcelable {
-    private byte[] mLeBluetoothDeviceAddress;
-    private byte[] mSecurityManagerTk;
-    private byte[] mLeSecureConnectionsConfirmation;
-    private byte[] mLeSecureConnectionsRandom;
+@SystemApi
+public final class OobData implements Parcelable {
 
-    public byte[] getLeBluetoothDeviceAddress() {
-        return mLeBluetoothDeviceAddress;
+    private static final String TAG = "OobData";
+    /** The {@link OobData#mClassicLength} may be. (AD 3.1.1) (CSS 1.6.2) @hide */
+    @SystemApi
+    public static final int OOB_LENGTH_OCTETS = 2;
+    /**
+     * The length for the {@link OobData#mDeviceAddressWithType}(6) and Address Type(1).
+     * (AD 3.1.2) (CSS 1.6.2)
+     * @hide
+     */
+    @SystemApi
+    public static final int DEVICE_ADDRESS_OCTETS = 7;
+    /** The Class of Device is 3 octets. (AD 3.1.3) (CSS 1.6.2) @hide */
+    @SystemApi
+    public static final int CLASS_OF_DEVICE_OCTETS = 3;
+    /** The Confirmation data must be 16 octets. (AD 3.2.2) (CSS 1.6.2) @hide */
+    @SystemApi
+    public static final int CONFIRMATION_OCTETS = 16;
+    /** The Randomizer data must be 16 octets. (AD 3.2.3) (CSS 1.6.2) @hide */
+    @SystemApi
+    public static final int RANDOMIZER_OCTETS = 16;
+    /** The LE Device Role length is 1 octet. (AD 3.3.2) (CSS 1.17) @hide */
+    @SystemApi
+    public static final int LE_DEVICE_ROLE_OCTETS = 1;
+    /** The {@link OobData#mLeTemporaryKey} length. (3.4.1) @hide */
+    @SystemApi
+    public static final int LE_TK_OCTETS = 16;
+    /** The {@link OobData#mLeAppearance} length. (3.4.1) @hide */
+    @SystemApi
+    public static final int LE_APPEARANCE_OCTETS = 2;
+    /** The {@link OobData#mLeFlags} length. (3.4.1) @hide */
+    @SystemApi
+    public static final int LE_DEVICE_FLAG_OCTETS = 1; // 1 octet to hold the 0-4 value.
+
+    // Le Roles
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+        prefix = { "LE_DEVICE_ROLE_" },
+        value = {
+            LE_DEVICE_ROLE_PERIPHERAL_ONLY,
+            LE_DEVICE_ROLE_CENTRAL_ONLY,
+            LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL,
+            LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL
+        }
+    )
+    public @interface LeRole {}
+
+    /** @hide */
+    @SystemApi
+    public static final int LE_DEVICE_ROLE_PERIPHERAL_ONLY = 0x00;
+    /** @hide */
+    @SystemApi
+    public static final int LE_DEVICE_ROLE_CENTRAL_ONLY = 0x01;
+    /** @hide */
+    @SystemApi
+    public static final int LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL = 0x02;
+    /** @hide */
+    @SystemApi
+    public static final int LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL = 0x03;
+
+    // Le Flags
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+        prefix = { "LE_FLAG_" },
+        value = {
+            LE_FLAG_LIMITED_DISCOVERY_MODE,
+            LE_FLAG_GENERAL_DISCOVERY_MODE,
+            LE_FLAG_BREDR_NOT_SUPPORTED,
+            LE_FLAG_SIMULTANEOUS_CONTROLLER,
+            LE_FLAG_SIMULTANEOUS_HOST
+        }
+    )
+    public @interface LeFlag {}
+
+    /** @hide */
+    @SystemApi
+    public static final int LE_FLAG_LIMITED_DISCOVERY_MODE = 0x00;
+    /** @hide */
+    @SystemApi
+    public static final int LE_FLAG_GENERAL_DISCOVERY_MODE = 0x01;
+    /** @hide */
+    @SystemApi
+    public static final int LE_FLAG_BREDR_NOT_SUPPORTED = 0x02;
+    /** @hide */
+    @SystemApi
+    public static final int LE_FLAG_SIMULTANEOUS_CONTROLLER = 0x03;
+    /** @hide */
+    @SystemApi
+    public static final int LE_FLAG_SIMULTANEOUS_HOST = 0x04;
+
+    /**
+     * Main creation method for creating a Classic version of {@link OobData}.
+     *
+     * <p>This object will allow the caller to call {@link ClassicBuilder#build()}
+     * to build the data object or add any option information to the builder.
+     *
+     * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS} octets
+     * of data. Data is derived from controller/host stack and is required for pairing OOB.
+     * @param classicLength byte array representing the length of data from 8-65535 across 2
+     * octets (0xXXXX).
+     * @param deviceAddressWithType byte array representing the Bluetooth Address of the device
+     * that owns the OOB data. (i.e. the originator) [6 octets]
+     *
+     * @return a Classic Builder instance with all the given data set or null.
+     *
+     * @throws IllegalArgumentException if any of the values fail to be set.
+     * @throws NullPointerException if any argument is null.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public static ClassicBuilder createClassicBuilder(@NonNull byte[] confirmationHash,
+            @NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType) {
+        return new ClassicBuilder(confirmationHash, classicLength, deviceAddressWithType);
     }
 
     /**
-     * Sets the LE Bluetooth Device Address value to be used during LE pairing.
-     * The value shall be 7 bytes. Please see Bluetooth CSSv6, Part A 1.16 for
-     * a detailed description.
+     * Main creation method for creating a LE version of {@link OobData}.
+     *
+     * <p>This object will allow the caller to call {@link LeBuilder#build()}
+     * to build the data object or add any option information to the builder.
+     *
+     * @param deviceAddressWithType the LE device address plus the address type (7 octets);
+     * not null.
+     * @param leDeviceRole whether the device supports Peripheral, Central,
+     * Both including preference; not null. (1 octet)
+     * @param confirmationHash Array consisting of {@link OobData#CONFIRMATION_OCTETS} octets
+     * of data. Data is derived from controller/host stack and is
+     * required for pairing OOB.
+     *
+     * <p>Possible LE Device Role Values:
+     * 0x00 Only Peripheral supported
+     * 0x01 Only Central supported
+     * 0x02 Central & Peripheral supported; Peripheral Preferred
+     * 0x03 Only peripheral supported; Central Preferred
+     * 0x04 - 0xFF Reserved
+     *
+     * @return a LeBuilder instance with all the given data set or null.
+     *
+     * @throws IllegalArgumentException if any of the values fail to be set.
+     * @throws NullPointerException if any argument is null.
+     *
+     * @hide
      */
-    public void setLeBluetoothDeviceAddress(byte[] leBluetoothDeviceAddress) {
-        mLeBluetoothDeviceAddress = leBluetoothDeviceAddress;
-    }
-
-    public byte[] getSecurityManagerTk() {
-        return mSecurityManagerTk;
+    @NonNull
+    @SystemApi
+    public static LeBuilder createLeBuilder(@NonNull byte[] confirmationHash,
+            @NonNull byte[] deviceAddressWithType, @LeRole int leDeviceRole) {
+        return new LeBuilder(confirmationHash, deviceAddressWithType, leDeviceRole);
     }
 
     /**
-     * Sets the Temporary Key value to be used by the LE Security Manager during
-     * LE pairing. The value shall be 16 bytes. Please see Bluetooth CSSv6,
-     * Part A 1.8 for a detailed description.
+     * Builds an {@link OobData} object and validates that the required combination
+     * of values are present to create the LE specific OobData type.
+     *
+     * @hide
      */
-    public void setSecurityManagerTk(byte[] securityManagerTk) {
-        mSecurityManagerTk = securityManagerTk;
+    @SystemApi
+    public static final class LeBuilder {
+
+        /**
+         * It is recommended that this Hash C is generated anew for each
+         * pairing.
+         *
+         * <p>It should be noted that on passive NFC this isn't possible as the data is static
+         * and immutable.
+         */
+        private byte[] mConfirmationHash = null;
+
+        /**
+         * Optional, but adds more validity to the pairing.
+         *
+         * <p>If not present a value of 0 is assumed.
+         */
+        private byte[] mRandomizerHash = new byte[] {
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        };
+
+        /**
+         * The Bluetooth Device user-friendly name presented over Bluetooth Technology.
+         *
+         * <p>This is the name that may be displayed to the device user as part of the UI.
+         */
+        private byte[] mDeviceName = null;
+
+        /**
+         * Sets the Bluetooth Device name to be used for UI purposes.
+         *
+         * <p>Optional attribute.
+         *
+         * @param deviceName byte array representing the name, may be 0 in length, not null.
+         *
+         * @return {@link OobData#ClassicBuilder}
+         *
+         * @throws NullPointerException if deviceName is null.
+         *
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public LeBuilder setDeviceName(@NonNull byte[] deviceName) {
+            Preconditions.checkNotNull(deviceName);
+            this.mDeviceName = deviceName;
+            return this;
+        }
+
+        /**
+         * The Bluetooth Device Address is the address to which the OOB data belongs.
+         *
+         * <p>The length MUST be {@link OobData#DEVICE_ADDRESS_OCTETS} octets.
+         *
+         * <p> Address is encoded in Little Endian order.
+         *
+         * <p>e.g. 00:01:02:03:04:05 would be x05x04x03x02x01x00
+         */
+        private final byte[] mDeviceAddressWithType;
+
+        /**
+         * During an LE connection establishment, one must be in the Peripheral mode and the other
+         * in the Central role.
+         *
+         * <p>Possible Values:
+         * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported
+         * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported
+         * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported;
+         * Peripheral Preferred
+         * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred
+         * 0x04 - 0xFF Reserved
+         */
+        private final @LeRole int mLeDeviceRole;
+
+        /**
+         * Temporary key value from the Security Manager.
+         *
+         * <p> Must be {@link LE_TK_OCTETS} in size
+         */
+        private byte[] mLeTemporaryKey = null;
+
+        /**
+         * Defines the representation of the external appearance of the device.
+         *
+         * <p>For example, a mouse, remote control, or keyboard.
+         *
+         * <p>Used for visual on discovering device to represent icon/string/etc...
+         */
+        private byte[] mLeAppearance = null;
+
+        /**
+         * Contains which discoverable mode to use, BR/EDR support and capability.
+         *
+         * <p>Possible LE Flags:
+         * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode.
+         * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode.
+         * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of
+         * LMP Feature Mask Definitions.
+         * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to
+         * Same Device Capable (Controller).
+         * Bit 49 of LMP Feature Mask Definitions.
+         * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to
+         * Same Device Capable (Host).
+         * Bit 55 of LMP Feature Mask Definitions.
+         * <b>0x05- 0x07 Reserved</b>
+         */
+        private @LeFlag int mLeFlags = LE_FLAG_GENERAL_DISCOVERY_MODE; // Invalid default
+
+        /**
+         * Constructing an OobData object for use with LE requires
+         * a LE Device Address and LE Device Role as well as the Confirmation
+         * and optionally, the Randomizer, however it is recommended to use.
+         *
+         * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS}
+         * octets of data. Data is derived from controller/host stack and is required for
+         * pairing OOB.
+         * @param deviceAddressWithType 7 bytes containing the 6 byte address with the 1 byte
+         * address type.
+         * @param leDeviceRole indicating device's role and preferences (Central or Peripheral)
+         *
+         * <p>Possible Values:
+         * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported
+         * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported
+         * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported;
+         * Peripheral Preferred
+         * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred
+         * 0x04 - 0xFF Reserved
+         *
+         * @throws IllegalArgumentException if deviceAddressWithType is not
+         *                                  {@link LE_DEVICE_ADDRESS_OCTETS} octets
+         * @throws NullPointerException if any argument is null.
+         */
+        private LeBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] deviceAddressWithType,
+                @LeRole int leDeviceRole) {
+            Preconditions.checkNotNull(confirmationHash);
+            Preconditions.checkNotNull(deviceAddressWithType);
+            if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) {
+                throw new IllegalArgumentException("confirmationHash must be "
+                    + OobData.CONFIRMATION_OCTETS + " octets in length.");
+            }
+            this.mConfirmationHash = confirmationHash;
+            if (deviceAddressWithType.length != OobData.DEVICE_ADDRESS_OCTETS) {
+                throw new IllegalArgumentException("confirmationHash must be "
+                    + OobData.DEVICE_ADDRESS_OCTETS+ " octets in length.");
+            }
+            this.mDeviceAddressWithType = deviceAddressWithType;
+            if (leDeviceRole < LE_DEVICE_ROLE_PERIPHERAL_ONLY
+                    || leDeviceRole > LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL) {
+                throw new IllegalArgumentException("leDeviceRole must be a valid value.");
+            }
+            this.mLeDeviceRole = leDeviceRole;
+        }
+
+        /**
+         * Sets the Temporary Key value to be used by the LE Security Manager during
+         * LE pairing.
+         *
+         * @param leTemporaryKey byte array that shall be 16 bytes. Please see Bluetooth CSSv6,
+         * Part A 1.8 for a detailed description.
+         *
+         * @return {@link OobData#Builder}
+         *
+         * @throws IllegalArgumentException if the leTemporaryKey is an invalid format.
+         * @throws NullinterException if leTemporaryKey is null.
+         *
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public LeBuilder setLeTemporaryKey(@NonNull byte[] leTemporaryKey) {
+            Preconditions.checkNotNull(leTemporaryKey);
+            if (leTemporaryKey.length != LE_TK_OCTETS) {
+                throw new IllegalArgumentException("leTemporaryKey must be "
+                        + LE_TK_OCTETS + " octets in length.");
+            }
+            this.mLeTemporaryKey = leTemporaryKey;
+            return this;
+        }
+
+        /**
+         * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets
+         * of data. Data is derived from controller/host stack and is required for pairing OOB.
+         * Also, randomizerHash may be all 0s or null in which case it becomes all 0s.
+         *
+         * @throws IllegalArgumentException if null or incorrect length randomizerHash was passed.
+         * @throws NullPointerException if randomizerHash is null.
+         *
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public LeBuilder setRandomizerHash(@NonNull byte[] randomizerHash) {
+            Preconditions.checkNotNull(randomizerHash);
+            if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) {
+                throw new IllegalArgumentException("randomizerHash must be "
+                    + OobData.RANDOMIZER_OCTETS + " octets in length.");
+            }
+            this.mRandomizerHash = randomizerHash;
+            return this;
+        }
+
+        /**
+         * Sets the LE Flags necessary for the pairing scenario or discovery mode.
+         *
+         * @param leFlags enum value representing the 1 octet of data about discovery modes.
+         *
+         * <p>Possible LE Flags:
+         * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode.
+         * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode.
+         * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of
+         * LMP Feature Mask Definitions.
+         * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to
+         * Same Device Capable (Controller) Bit 49 of LMP Feature Mask Definitions.
+         * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to
+         * Same Device Capable (Host).
+         * Bit 55 of LMP Feature Mask Definitions.
+         * 0x05- 0x07 Reserved
+         *
+         * @throws IllegalArgumentException for invalid flag
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public LeBuilder setLeFlags(@LeFlag int leFlags) {
+            if (leFlags < LE_FLAG_LIMITED_DISCOVERY_MODE || leFlags > LE_FLAG_SIMULTANEOUS_HOST) {
+                throw new IllegalArgumentException("leFlags must be a valid value.");
+            }
+            this.mLeFlags = leFlags;
+            return this;
+        }
+
+        /**
+         * Validates and builds the {@link OobData} object for LE Security.
+         *
+         * @return {@link OobData} with given builder values
+         *
+         * @throws IllegalStateException if either of the 2 required fields were not set.
+         *
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public OobData build() {
+            final OobData oob =
+                    new OobData(this.mDeviceAddressWithType, this.mLeDeviceRole,
+                            this.mConfirmationHash);
+
+            // If we have values, set them, otherwise use default
+            oob.mLeTemporaryKey =
+                    (this.mLeTemporaryKey != null) ? this.mLeTemporaryKey : oob.mLeTemporaryKey;
+            oob.mLeAppearance = (this.mLeAppearance != null)
+                    ? this.mLeAppearance : oob.mLeAppearance;
+            oob.mLeFlags = (this.mLeFlags != 0xF) ? this.mLeFlags : oob.mLeFlags;
+            oob.mDeviceName = (this.mDeviceName != null) ? this.mDeviceName : oob.mDeviceName;
+            oob.mRandomizerHash = this.mRandomizerHash;
+            return oob;
+        }
     }
 
-    public byte[] getLeSecureConnectionsConfirmation() {
-        return mLeSecureConnectionsConfirmation;
+    /**
+     * Builds an {@link OobData} object and validates that the required combination
+     * of values are present to create the Classic specific OobData type.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class ClassicBuilder {
+        // Used by both Classic and LE
+        /**
+         * It is recommended that this Hash C is generated anew for each
+         * pairing.
+         *
+         * <p>It should be noted that on passive NFC this isn't possible as the data is static
+         * and immutable.
+         *
+         * @hide
+         */
+        private byte[] mConfirmationHash = null;
+
+        /**
+         * Optional, but adds more validity to the pairing.
+         *
+         * <p>If not present a value of 0 is assumed.
+         *
+         * @hide
+         */
+        private byte[] mRandomizerHash = new byte[] {
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        };
+
+        /**
+         * The Bluetooth Device user-friendly name presented over Bluetooth Technology.
+         *
+         * <p>This is the name that may be displayed to the device user as part of the UI.
+         *
+         * @hide
+         */
+        private byte[] mDeviceName = null;
+
+        /**
+         * This length value provides the absolute length of total OOB data block used for
+         * Bluetooth BR/EDR
+         *
+         * <p>OOB communication, which includes the length field itself and the Bluetooth
+         * Device Address.
+         *
+         * <p>The minimum length that may be represented in this field is 8.
+         *
+         * @hide
+         */
+        private final byte[] mClassicLength;
+
+        /**
+         * The Bluetooth Device Address is the address to which the OOB data belongs.
+         *
+         * <p>The length MUST be {@link OobData#DEVICE_ADDRESS_OCTETS} octets.
+         *
+         * <p> Address is encoded in Little Endian order.
+         *
+         * <p>e.g. 00:01:02:03:04:05 would be x05x04x03x02x01x00
+         *
+         * @hide
+         */
+        private final byte[] mDeviceAddressWithType;
+
+        /**
+         * Class of Device information is to be used to provide a graphical representation
+         * to the user as part of UI involving operations.
+         *
+         * <p>This is not to be used to determine a particular service can be used.
+         *
+         * <p>The length MUST be {@link OobData#CLASS_OF_DEVICE_OCTETS} octets.
+         *
+         * @hide
+         */
+        private byte[] mClassOfDevice = null;
+
+        /**
+         * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS}
+         * octets of data. Data is derived from controller/host stack and is required for pairing
+         * OOB.
+         * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets
+         * of data. Data is derived from controller/host stack and is required
+         * for pairing OOB. Also, randomizerHash may be all 0s or null in which case
+         * it becomes all 0s.
+         * @param classicLength byte array representing the length of data from 8-65535 across 2
+         * octets (0xXXXX). Inclusive of this value in the length.
+         * @param deviceAddressWithType byte array representing the Bluetooth Address of the device
+         * that owns the OOB data. (i.e. the originator) [7 octets] this includes the Address Type
+         * as the last octet.
+         *
+         * @throws IllegalArgumentException if any value is not the correct length
+         * @throws NullPointerException if anything passed is null
+         *
+         * @hide
+         */
+        private ClassicBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] classicLength,
+                @NonNull byte[] deviceAddressWithType) {
+            Preconditions.checkNotNull(confirmationHash);
+            Preconditions.checkNotNull(classicLength);
+            Preconditions.checkNotNull(deviceAddressWithType);
+            if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) {
+                throw new IllegalArgumentException("confirmationHash must be "
+                    + OobData.CONFIRMATION_OCTETS + " octets in length.");
+            }
+            this.mConfirmationHash = confirmationHash;
+            if (classicLength.length != OOB_LENGTH_OCTETS) {
+                throw new IllegalArgumentException("classicLength must be "
+                        + OOB_LENGTH_OCTETS + " octets in length.");
+            }
+            this.mClassicLength = classicLength;
+            if (deviceAddressWithType.length != DEVICE_ADDRESS_OCTETS) {
+                throw new IllegalArgumentException("deviceAddressWithType must be "
+                        + DEVICE_ADDRESS_OCTETS + " octets in length.");
+            }
+            this.mDeviceAddressWithType = deviceAddressWithType;
+        }
+
+        /**
+         * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets
+         * of data. Data is derived from controller/host stack and is required for pairing OOB.
+         * Also, randomizerHash may be all 0s or null in which case it becomes all 0s.
+         *
+         * @throws IllegalArgumentException if null or incorrect length randomizerHash was passed.
+         * @throws NullPointerException if randomizerHash is null.
+         *
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public ClassicBuilder setRandomizerHash(@NonNull byte[] randomizerHash) {
+            Preconditions.checkNotNull(randomizerHash);
+            if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) {
+                throw new IllegalArgumentException("randomizerHash must be "
+                    + OobData.RANDOMIZER_OCTETS + " octets in length.");
+            }
+            this.mRandomizerHash = randomizerHash;
+            return this;
+        }
+
+        /**
+         * Sets the Bluetooth Device name to be used for UI purposes.
+         *
+         * <p>Optional attribute.
+         *
+         * @param deviceName byte array representing the name, may be 0 in length, not null.
+         *
+         * @return {@link OobData#ClassicBuilder}
+         *
+         * @throws NullPointerException if deviceName is null
+         *
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public ClassicBuilder setDeviceName(@NonNull byte[] deviceName) {
+            Preconditions.checkNotNull(deviceName);
+            this.mDeviceName = deviceName;
+            return this;
+        }
+
+        /**
+         * Sets the Bluetooth Class of Device; used for UI purposes only.
+         *
+         * <p>Not an indicator of available services!
+         *
+         * <p>Optional attribute.
+         *
+         * @param classOfDevice byte array of {@link OobData#CLASS_OF_DEVICE_OCTETS} octets.
+         *
+         * @return {@link OobData#ClassicBuilder}
+         *
+         * @throws IllegalArgumentException if length is not equal to
+         * {@link OobData#CLASS_OF_DEVICE_OCTETS} octets.
+         * @throws NullPointerException if classOfDevice is null.
+         *
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public ClassicBuilder setClassOfDevice(@NonNull byte[] classOfDevice) {
+            Preconditions.checkNotNull(classOfDevice);
+            if (classOfDevice.length != OobData.CLASS_OF_DEVICE_OCTETS) {
+                throw new IllegalArgumentException("classOfDevice must be "
+                        + OobData.CLASS_OF_DEVICE_OCTETS + " octets in length.");
+            }
+            this.mClassOfDevice = classOfDevice;
+            return this;
+        }
+
+        /**
+         * Validates and builds the {@link OobDat object for Classic Security.
+         *
+         * @return {@link OobData} with previously given builder values.
+         *
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public OobData build() {
+            final OobData oob =
+                    new OobData(this.mClassicLength, this.mDeviceAddressWithType,
+                            this.mConfirmationHash);
+            // If we have values, set them, otherwise use default
+            oob.mDeviceName = (this.mDeviceName != null) ? this.mDeviceName : oob.mDeviceName;
+            oob.mClassOfDevice = (this.mClassOfDevice != null)
+                    ? this.mClassOfDevice : oob.mClassOfDevice;
+            oob.mRandomizerHash = this.mRandomizerHash;
+            return oob;
+        }
     }
 
-    public void setLeSecureConnectionsConfirmation(byte[] leSecureConnectionsConfirmation) {
-        mLeSecureConnectionsConfirmation = leSecureConnectionsConfirmation;
+    // Members (Defaults for Optionals must be set or Parceling fails on NPE)
+    // Both
+    private final byte[] mDeviceAddressWithType;
+    private final byte[] mConfirmationHash;
+    private byte[] mRandomizerHash = new byte[] {
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    };
+    // Default the name to "Bluetooth Device"
+    private byte[] mDeviceName = new byte[] {
+        // Bluetooth
+        0x42, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68,
+        // <space>Device
+        0x20, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65
+    };
+
+    // Classic
+    private final byte[] mClassicLength;
+    private byte[] mClassOfDevice = new byte[CLASS_OF_DEVICE_OCTETS];
+
+    // LE
+    private final @LeRole int mLeDeviceRole;
+    private byte[] mLeTemporaryKey = new byte[LE_TK_OCTETS];
+    private byte[] mLeAppearance = new byte[LE_APPEARANCE_OCTETS];
+    private @LeFlag int mLeFlags = LE_FLAG_LIMITED_DISCOVERY_MODE;
+
+    /**
+     * @return byte array representing the MAC address of a bluetooth device.
+     * The Address is 6 octets long with a 1 octet address type associated with the address.
+     *
+     * <p>For classic this will be 6 byte address plus the default of PUBLIC_ADDRESS Address Type.
+     * For LE there are more choices for Address Type.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public byte[] getDeviceAddressWithType() {
+        return mDeviceAddressWithType;
     }
 
-    public byte[] getLeSecureConnectionsRandom() {
-        return mLeSecureConnectionsRandom;
+    /**
+     * @return byte array representing the confirmationHash value
+     * which is used to confirm the identity to the controller.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public byte[] getConfirmationHash() {
+        return mConfirmationHash;
     }
 
-    public void setLeSecureConnectionsRandom(byte[] leSecureConnectionsRandom) {
-        mLeSecureConnectionsRandom = leSecureConnectionsRandom;
+    /**
+     * @return byte array representing the randomizerHash value
+     * which is used to verify the identity of the controller.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public byte[] getRandomizerHash() {
+        return mRandomizerHash;
     }
 
-    public OobData() {
+    /**
+     * @return Device Name used for displaying name in UI.
+     *
+     * <p>Also, this will be populated with the LE Local Name if the data is for LE.
+     *
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    public byte[] getDeviceName() {
+        return mDeviceName;
+    }
+
+    /**
+     * @return byte array representing the oob data length which is the length
+     * of all of the data including these octets.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public byte[] getClassicLength() {
+        return mClassicLength;
+    }
+
+    /**
+     * @return byte array representing the class of device for UI display.
+     *
+     * <p>Does not indicate services available; for display only.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public byte[] getClassOfDevice() {
+        return mClassOfDevice;
+    }
+
+    /**
+     * @return Temporary Key used for LE pairing.
+     *
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    public byte[] getLeTemporaryKey() {
+        return mLeTemporaryKey;
+    }
+
+    /**
+     * @return Appearance used for LE pairing. For use in UI situations
+     * when determining what sort of icons or text to display regarding
+     * the device.
+     *
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    public byte[] getLeAppearance() {
+        return mLeTemporaryKey;
+    }
+
+    /**
+     * @return Flags used to determing discoverable mode to use, BR/EDR Support, and Capability.
+     *
+     * <p>Possible LE Flags:
+     * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode.
+     * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode.
+     * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of
+     * LMP Feature Mask Definitions.
+     * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to
+     * Same Device Capable (Controller).
+     * Bit 49 of LMP Feature Mask Definitions.
+     * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to
+     * Same Device Capable (Host).
+     * Bit 55 of LMP Feature Mask Definitions.
+     * <b>0x05- 0x07 Reserved</b>
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    @LeFlag
+    public int getLeFlags() {
+        return mLeFlags;
+    }
+
+    /**
+     * @return the supported and preferred roles of the LE device.
+     *
+     * <p>Possible Values:
+     * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported
+     * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported
+     * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported;
+     * Peripheral Preferred
+     * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred
+     * 0x04 - 0xFF Reserved
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    @LeRole
+    public int getLeDeviceRole() {
+        return mLeDeviceRole;
+    }
+
+    /**
+     * Classic Security Constructor
+     */
+    private OobData(@NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType,
+            @NonNull byte[] confirmationHash) {
+        mClassicLength = classicLength;
+        mDeviceAddressWithType = deviceAddressWithType;
+        mConfirmationHash = confirmationHash;
+        mLeDeviceRole = -1; // Satisfy final
+    }
+
+    /**
+     * LE Security Constructor
+     */
+    private OobData(@NonNull byte[] deviceAddressWithType, @LeRole int leDeviceRole,
+            @NonNull byte[] confirmationHash) {
+        mDeviceAddressWithType = deviceAddressWithType;
+        mLeDeviceRole = leDeviceRole;
+        mConfirmationHash = confirmationHash;
+        mClassicLength = new byte[OOB_LENGTH_OCTETS]; // Satisfy final
     }
 
     private OobData(Parcel in) {
-        mLeBluetoothDeviceAddress = in.createByteArray();
-        mSecurityManagerTk = in.createByteArray();
-        mLeSecureConnectionsConfirmation = in.createByteArray();
-        mLeSecureConnectionsRandom = in.createByteArray();
+        // Both
+        mDeviceAddressWithType = in.createByteArray();
+        mConfirmationHash = in.createByteArray();
+        mRandomizerHash = in.createByteArray();
+        mDeviceName = in.createByteArray();
+
+        // Classic
+        mClassicLength = in.createByteArray();
+        mClassOfDevice = in.createByteArray();
+
+        // LE
+        mLeDeviceRole = in.readInt();
+        mLeTemporaryKey = in.createByteArray();
+        mLeAppearance = in.createByteArray();
+        mLeFlags = in.readInt();
     }
 
+    /**
+     * @hide
+     */
     @Override
     public int describeContents() {
         return 0;
     }
 
+    /**
+     * @hide
+     */
     @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeByteArray(mLeBluetoothDeviceAddress);
-        out.writeByteArray(mSecurityManagerTk);
-        out.writeByteArray(mLeSecureConnectionsConfirmation);
-        out.writeByteArray(mLeSecureConnectionsRandom);
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        // Both
+        // Required
+        out.writeByteArray(mDeviceAddressWithType);
+        // Required
+        out.writeByteArray(mConfirmationHash);
+        // Optional
+        out.writeByteArray(mRandomizerHash);
+        // Optional
+        out.writeByteArray(mDeviceName);
+
+        // Classic
+        // Required
+        out.writeByteArray(mClassicLength);
+        // Optional
+        out.writeByteArray(mClassOfDevice);
+
+        // LE
+        // Required
+        out.writeInt(mLeDeviceRole);
+        // Required
+        out.writeByteArray(mLeTemporaryKey);
+        // Optional
+        out.writeByteArray(mLeAppearance);
+        // Optional
+        out.writeInt(mLeFlags);
     }
 
+    // For Parcelable
     public static final @android.annotation.NonNull Parcelable.Creator<OobData> CREATOR =
             new Parcelable.Creator<OobData>() {
         public OobData createFromParcel(Parcel in) {
@@ -108,4 +969,47 @@
             return new OobData[size];
         }
     };
+
+    /**
+     * @return a {@link String} representation of the OobData object.
+     *
+     * @hide
+     */
+    @Override
+    @NonNull
+    public String toString() {
+        return "OobData: \n\t"
+            // Both
+            + "Device Address With Type: " +  toHexString(mDeviceAddressWithType) + "\n\t"
+            + "Confirmation: " + toHexString(mConfirmationHash) + "\n\t"
+            + "Randomizer: " + toHexString(mRandomizerHash) + "\n\t"
+            + "Device Name: " + toHexString(mDeviceName) + "\n\t"
+            // Classic
+            + "OobData Length: " +  toHexString(mClassicLength) + "\n\t"
+            + "Class of Device: " +  toHexString(mClassOfDevice) + "\n\t"
+            // LE
+            + "LE Device Role: " + toHexString(mLeDeviceRole) + "\n\t"
+            + "LE Temporary Key: " + toHexString(mLeTemporaryKey) + "\n\t"
+            + "LE Appearance: " + toHexString(mLeAppearance) + "\n\t"
+            + "LE Flags: " + toHexString(mLeFlags) + "\n\t";
+    }
+
+    @NonNull
+    private String toHexString(@NonNull int b) {
+        return toHexString(new byte[] {(byte) b});
+    }
+
+    @NonNull
+    private String toHexString(@NonNull byte b) {
+        return toHexString(new byte[] {b});
+    }
+
+    @NonNull
+    private String toHexString(@NonNull byte[] array) {
+        StringBuilder builder = new StringBuilder(array.length * 2);
+        for (byte b: array) {
+            builder.append(String.format("%02x", b));
+        }
+        return builder.toString();
+    }
 }
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index 7511fd0..3c20dca 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -16,15 +16,19 @@
 
 package android.bluetooth.le;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothDevice.AddressType;
 import android.os.Parcel;
 import android.os.ParcelUuid;
 import android.os.Parcelable;
 
 import com.android.internal.util.BitUtils;
+import com.android.internal.util.Preconditions;
 
 import java.util.Arrays;
 import java.util.List;
@@ -53,6 +57,11 @@
     @Nullable
     private final String mDeviceAddress;
 
+    private final @AddressType int mAddressType;
+
+    @Nullable
+    private final byte[] mIrk;
+
     @Nullable
     private final ParcelUuid mServiceUuid;
     @Nullable
@@ -79,12 +88,12 @@
     /** @hide */
     public static final ScanFilter EMPTY = new ScanFilter.Builder().build();
 
-
     private ScanFilter(String name, String deviceAddress, ParcelUuid uuid,
             ParcelUuid uuidMask, ParcelUuid solicitationUuid,
             ParcelUuid solicitationUuidMask, ParcelUuid serviceDataUuid,
             byte[] serviceData, byte[] serviceDataMask,
-            int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask) {
+            int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask,
+            @AddressType int addressType, @Nullable byte[] irk) {
         mDeviceName = name;
         mServiceUuid = uuid;
         mServiceUuidMask = uuidMask;
@@ -97,6 +106,8 @@
         mManufacturerId = manufacturerId;
         mManufacturerData = manufacturerData;
         mManufacturerDataMask = manufacturerDataMask;
+        mAddressType = addressType;
+        mIrk = irk;
     }
 
     @Override
@@ -280,6 +291,23 @@
         return mDeviceAddress;
     }
 
+    /**
+     * @hide
+     */
+    @SystemApi
+    public @AddressType int getAddressType() {
+        return mAddressType;
+    }
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public byte[] getIrk() {
+        return mIrk;
+    }
+
     @Nullable
     public byte[] getServiceData() {
         return mServiceData;
@@ -516,8 +544,16 @@
      */
     public static final class Builder {
 
+        /**
+         * @hide
+         */
+        @SystemApi
+        public static final int LEN_IRK_OCTETS = 16;
+
         private String mDeviceName;
         private String mDeviceAddress;
+        private @AddressType int mAddressType = BluetoothDevice.ADDRESS_TYPE_PUBLIC;
+        private byte[] mIrk;
 
         private ParcelUuid mServiceUuid;
         private ParcelUuid mUuidMask;
@@ -546,14 +582,130 @@
          *
          * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the
          * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link
-         * BluetoothAdapter#checkBluetoothAddress}.
+         * BluetoothAdapter#checkBluetoothAddress}.  The @AddressType is defaulted to {@link
+         * BluetoothDevice#ADDRESS_TYPE_PUBLIC}
          * @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
          */
         public Builder setDeviceAddress(String deviceAddress) {
-            if (deviceAddress != null && !BluetoothAdapter.checkBluetoothAddress(deviceAddress)) {
+            return setDeviceAddress(deviceAddress, BluetoothDevice.ADDRESS_TYPE_PUBLIC);
+        }
+
+        /**
+         * Set filter on Address with AddressType
+         *
+         * <p>This key is used to resolve a private address from a public address.
+         *
+         * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the
+         * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link
+         * BluetoothAdapter#checkBluetoothAddress}. May be any type of address.
+         * @param addressType indication of the type of address
+         * e.g. {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC}
+         * or {@link BluetoothDevice#ADDRESS_TYPE_RANDOM}
+         *
+         * @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
+         * @throws IllegalArgumentException If the {@code addressType} is invalid length
+         * @throws NullPointerException if {@code deviceAddress} is null.
+         *
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public Builder setDeviceAddress(@NonNull String deviceAddress,
+                                        @AddressType int addressType) {
+            return setDeviceAddressInternal(deviceAddress, addressType, null);
+        }
+
+        /**
+         * Set filter on Address with AddressType and the Identity Resolving Key (IRK).
+         *
+         * <p>The IRK is used to resolve a {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC} from
+         * a PRIVATE_ADDRESS type.
+         *
+         * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the
+         * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link
+         * BluetoothAdapter#checkBluetoothAddress}.  This Address type must only be PUBLIC OR RANDOM
+         * STATIC.
+         * @param addressType indication of the type of address
+         * e.g. {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC}
+         * or {@link BluetoothDevice#ADDRESS_TYPE_RANDOM}
+         * @param irk non-null byte array representing the Identity Resolving Key
+         *
+         * @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
+         * @throws IllegalArgumentException if the {@code irk} is invalid length.
+         * @throws IllegalArgumentException If the {@code addressType} is invalid length or is not
+         * PUBLIC or RANDOM STATIC when an IRK is present.
+         * @throws NullPointerException if {@code deviceAddress} or {@code irk} is null.
+         *
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public Builder setDeviceAddress(@NonNull String deviceAddress,
+                                        @AddressType int addressType,
+                                        @NonNull byte[] irk) {
+            Preconditions.checkNotNull(irk);
+            if (irk.length != LEN_IRK_OCTETS) {
+                throw new IllegalArgumentException("'irk' is invalid length!");
+            }
+            return setDeviceAddressInternal(deviceAddress, addressType, irk);
+        }
+
+        /**
+         * Set filter on Address with AddressType and the Identity Resolving Key (IRK).
+         *
+         * <p>Internal setter for the device address
+         *
+         * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the
+         * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link
+         * BluetoothAdapter#checkBluetoothAddress}.
+         * @param addressType indication of the type of address
+         * e.g. {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC}
+         * @param irk non-null byte array representing the Identity Resolving Address; nullable
+         * internally.
+         *
+         * @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
+         * @throws IllegalArgumentException If the {@code addressType} is invalid length.
+         * @throws NullPointerException if {@code deviceAddress} is null.
+         *
+         * @hide
+         */
+        @NonNull
+        private Builder setDeviceAddressInternal(@NonNull String deviceAddress,
+                                                 @AddressType int addressType,
+                                                 @Nullable byte[] irk) {
+
+            // Make sure our deviceAddress is valid!
+            Preconditions.checkNotNull(deviceAddress);
+            if (!BluetoothAdapter.checkBluetoothAddress(deviceAddress)) {
                 throw new IllegalArgumentException("invalid device address " + deviceAddress);
             }
+
+            // Verify type range
+            if (addressType < BluetoothDevice.ADDRESS_TYPE_PUBLIC
+                || addressType > BluetoothDevice.ADDRESS_TYPE_RANDOM) {
+                throw new IllegalArgumentException("'addressType' is invalid!");
+            }
+
+            // IRK can only be used for a PUBLIC or RANDOM (STATIC) Address.
+            if (addressType == BluetoothDevice.ADDRESS_TYPE_RANDOM) {
+                // Don't want a bad combination of address and irk!
+                if (irk != null) {
+                    // Since there are 3 possible RANDOM subtypes we must check to make sure
+                    // the correct type of address is used.
+                    if (!BluetoothAdapter.isAddressRandomStatic(deviceAddress)) {
+                        throw new IllegalArgumentException(
+                                "Invalid combination: IRK requires either a PUBLIC or "
+                                + "RANDOM (STATIC) Address");
+                    }
+                }
+            }
+
+            // PUBLIC doesn't require extra work
+            // Without an IRK any address may be accepted
+
             mDeviceAddress = deviceAddress;
+            mAddressType = addressType;
+            mIrk = irk;
             return this;
         }
 
@@ -727,7 +879,8 @@
                     mServiceUuid, mUuidMask, mServiceSolicitationUuid,
                     mServiceSolicitationUuidMask,
                     mServiceDataUuid, mServiceData, mServiceDataMask,
-                    mManufacturerId, mManufacturerData, mManufacturerDataMask);
+                    mManufacturerId, mManufacturerData, mManufacturerDataMask,
+                    mAddressType, mIrk);
         }
     }
 }
diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java
index 504118e..368d1ee 100644
--- a/core/java/android/bluetooth/le/ScanSettings.java
+++ b/core/java/android/bluetooth/le/ScanSettings.java
@@ -52,6 +52,16 @@
     public static final int SCAN_MODE_LOW_LATENCY = 2;
 
     /**
+     * Perform Bluetooth LE scan in ambient discovery mode. This mode has lower duty cycle and more
+     * aggressive scan interval than balanced mode that provides a good trade-off between scan
+     * latency and power consumption.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int SCAN_MODE_AMBIENT_DISCOVERY = 3;
+
+    /**
      * Trigger a callback for every Bluetooth advertisement found that matches the filter criteria.
      * If no filter is active, all advertisement packets are reported.
      */
@@ -276,10 +286,17 @@
          * @throws IllegalArgumentException If the {@code scanMode} is invalid.
          */
         public Builder setScanMode(int scanMode) {
-            if (scanMode < SCAN_MODE_OPPORTUNISTIC || scanMode > SCAN_MODE_LOW_LATENCY) {
-                throw new IllegalArgumentException("invalid scan mode " + scanMode);
+            switch (scanMode) {
+                case SCAN_MODE_OPPORTUNISTIC:
+                case SCAN_MODE_LOW_POWER:
+                case SCAN_MODE_BALANCED:
+                case SCAN_MODE_LOW_LATENCY:
+                case SCAN_MODE_AMBIENT_DISCOVERY:
+                    mScanMode = scanMode;
+                    break;
+                default:
+                    throw new IllegalArgumentException("invalid scan mode " + scanMode);
             }
-            mScanMode = scanMode;
             return this;
         }
 
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 32aa037..d21462e 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -744,7 +744,7 @@
     // Always log queries which take 500ms+; shorter queries are
     // sampled accordingly.
     private static final boolean ENABLE_CONTENT_SAMPLE = false;
-    private static final int SLOW_THRESHOLD_MILLIS = 500;
+    private static final int SLOW_THRESHOLD_MILLIS = 500 * Build.HW_TIMEOUT_MULTIPLIER;
     private final Random mRandom = new Random();  // guarded by itself
 
     /** @hide */
@@ -758,7 +758,8 @@
      * before we decide it must be hung.
      * @hide
      */
-    public static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS = 10 * 1000;
+    public static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS =
+            10 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
 
     /**
      * How long we wait for an provider to be published. Should be longer than
@@ -766,10 +767,11 @@
      * @hide
      */
     public static final int CONTENT_PROVIDER_READY_TIMEOUT_MILLIS =
-            CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS + 10 * 1000;
+            CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS + 10 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
 
     // Timeout given a ContentProvider that has already been started and connected to.
-    private static final int CONTENT_PROVIDER_TIMEOUT_MILLIS = 3 * 1000;
+    private static final int CONTENT_PROVIDER_TIMEOUT_MILLIS =
+            3 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
 
     // Should be >= {@link #CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS}, because that's how
     // long ActivityManagerService is giving a content provider to get published if a new process
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 9c88566..fe9ed27 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3451,6 +3451,7 @@
             VIBRATOR_SERVICE,
             //@hide: STATUS_BAR_SERVICE,
             CONNECTIVITY_SERVICE,
+            PAC_PROXY_SERVICE,
             VCN_MANAGEMENT_SERVICE,
             //@hide: IP_MEMORY_STORE_SERVICE,
             IPSEC_SERVICE,
@@ -4017,6 +4018,17 @@
     public static final String CONNECTIVITY_SERVICE = "connectivity";
 
     /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.net.PacProxyManager} for handling management of
+     * pac proxy information.
+     *
+     * @see #getSystemService(String)
+     * @see android.net.PacProxyManager
+     * @hide
+     */
+    public static final String PAC_PROXY_SERVICE = "pac_proxy";
+
+    /**
      * Use with {@link #getSystemService(String)} to retrieve a {@link android.net.vcn.VcnManager}
      * for managing Virtual Carrier Networks
      *
@@ -4087,7 +4099,8 @@
      * @see #getSystemService(String)
      * @hide
      */
-    @TestApi public static final String TEST_NETWORK_SERVICE = "test_network";
+    @TestApi @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final String TEST_NETWORK_SERVICE = "test_network";
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a {@link
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6bfc12d..4d68e90 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2329,6 +2329,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final String ACTION_CLEAR_DNS_CACHE = "android.intent.action.CLEAR_DNS_CACHE";
     /**
      * Alarm Changed Action: This is broadcast when the AlarmClock
diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS
index d0d406a..01b554a 100644
--- a/core/java/android/content/OWNERS
+++ b/core/java/android/content/OWNERS
@@ -8,3 +8,5 @@
 per-file AutofillOptions* = file:/core/java/android/service/autofill/OWNERS
 per-file ContentCaptureOptions* = file:/core/java/android/service/contentcapture/OWNERS
 per-file LocusId* = file:/core/java/android/service/contentcapture/OWNERS
+per-file ComponentCallbacksController = file:/services/core/java/com/android/server/wm/OWNERS
+per-file ComponentCallbacksController = charlesccchen@google.com
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 7e17256..23f5e3a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3167,8 +3167,57 @@
     public static final String FEATURE_VR_HEADTRACKING = "android.hardware.vr.headtracking";
 
     /**
-     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
-     * The device has a StrongBox hardware-backed Keystore.
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature(String, int)}: If this feature is supported, the device implements
+     * the Android Keystore backed by an isolated execution environment. The version indicates
+     * which features are implemented in the isolated execution environment:
+     * <ul>
+     * <li>100: Hardware support for ECDH (see {@link javax.crypto.KeyAgreement}) and support
+     * for app-generated attestation keys (see {@link
+     * android.security.keystore.KeyGenParameterSpec.Builder#setAttestKeyAlias(String)}).
+     * <li>41: Hardware enforcement of device-unlocked keys (see {@link
+     * android.security.keystore.KeyGenParameterSpec.Builder#setUnlockedDeviceRequired(boolean)}).
+     * <li>40: Support for wrapped key import (see {@link
+     * android.security.keystore.WrappedKeyEntry}), optional support for ID attestation (see {@link
+     * android.security.keystore.KeyGenParameterSpec.Builder#setDevicePropertiesAttestationIncluded(boolean)}),
+     * attestation (see {@link
+     * android.security.keystore.KeyGenParameterSpec.Builder#setAttestationChallenge(byte[])}),
+     * AES, HMAC, ECDSA and RSA support where the secret or private key never leaves secure
+     * hardware, and support for requiring user authentication before a key can be used.
+     * </ul>
+     * This feature version is guaranteed to be set for all devices launching with Android 12 and
+     * may be set on devices launching with an earlier version. If the feature version is set, it
+     * will at least have the value 40. If it's not set the device may have a version of
+     * hardware-backed keystore but it may not support all features listed above.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_HARDWARE_KEYSTORE = "android.hardware.hardware_keystore";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures}, {@link #hasSystemFeature(String)}, and
+     * {@link #hasSystemFeature(String, int)}: If this feature is supported, the device implements
+     * the Android Keystore backed by a dedicated secure processor referred to as
+     * <a href="https://source.android.com/security/best-practices/hardware#strongbox-keymaster">
+     * StrongBox</a>. If this feature has a version, the version number indicates which features are
+     * implemented in StrongBox:
+     * <ul>
+     * <li>100: Hardware support for ECDH (see {@link javax.crypto.KeyAgreement}) and support
+     * for app-generated attestation keys (see {@link
+     * android.security.keystore.KeyGenParameterSpec.Builder#setAttestKeyAlias(String)}).
+     * <li>41: Hardware enforcement of device-unlocked keys (see {@link
+     * android.security.keystore.KeyGenParameterSpec.Builder#setUnlockedDeviceRequired(boolean)}).
+     * <li>40: Support for wrapped key import (see {@link
+     * android.security.keystore.WrappedKeyEntry}), optional support for ID attestation (see {@link
+     * android.security.keystore.KeyGenParameterSpec.Builder#setDevicePropertiesAttestationIncluded(boolean)}),
+     * attestation (see {@link
+     * android.security.keystore.KeyGenParameterSpec.Builder#setAttestationChallenge(byte[])}),
+     * AES, HMAC, ECDSA and RSA support where the secret or private key never leaves secure
+     * hardware, and support for requiring user authentication before a key can be used.
+     * </ul>
+     * If a device has StrongBox, this feature version number is guaranteed to be set for all
+     * devices launching with Android 12 and may be set on devices launching with an earlier
+     * version. If the feature version is set, it will at least have the value 40. If it's not
+     * set the device may have StrongBox but it may not support all features listed above.
      */
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_STRONGBOX_KEYSTORE =
diff --git a/core/java/android/ddm/DdmHandleHeap.java b/core/java/android/ddm/DdmHandleHeap.java
index e24aeb2..8fa2352 100644
--- a/core/java/android/ddm/DdmHandleHeap.java
+++ b/core/java/android/ddm/DdmHandleHeap.java
@@ -30,15 +30,7 @@
  */
 public class DdmHandleHeap extends ChunkHandler {
 
-    public static final int CHUNK_HPIF = type("HPIF");
-    public static final int CHUNK_HPSG = type("HPSG");
-    public static final int CHUNK_HPDU = type("HPDU");
-    public static final int CHUNK_HPDS = type("HPDS");
-    public static final int CHUNK_NHSG = type("NHSG");
     public static final int CHUNK_HPGC = type("HPGC");
-    public static final int CHUNK_REAE = type("REAE");
-    public static final int CHUNK_REAQ = type("REAQ");
-    public static final int CHUNK_REAL = type("REAL");
 
     private static DdmHandleHeap mInstance = new DdmHandleHeap();
 
@@ -50,15 +42,7 @@
      * Register for the messages we're interested in.
      */
     public static void register() {
-        DdmServer.registerHandler(CHUNK_HPIF, mInstance);
-        DdmServer.registerHandler(CHUNK_HPSG, mInstance);
-        DdmServer.registerHandler(CHUNK_HPDU, mInstance);
-        DdmServer.registerHandler(CHUNK_HPDS, mInstance);
-        DdmServer.registerHandler(CHUNK_NHSG, mInstance);
         DdmServer.registerHandler(CHUNK_HPGC, mInstance);
-        DdmServer.registerHandler(CHUNK_REAE, mInstance);
-        DdmServer.registerHandler(CHUNK_REAQ, mInstance);
-        DdmServer.registerHandler(CHUNK_REAL, mInstance);
     }
 
     /**
@@ -81,24 +65,8 @@
             Log.v("ddm-heap", "Handling " + name(request.type) + " chunk");
         int type = request.type;
 
-        if (type == CHUNK_HPIF) {
-            return handleHPIF(request);
-        } else if (type == CHUNK_HPSG) {
-            return handleHPSGNHSG(request, false);
-        } else if (type == CHUNK_HPDU) {
-            return handleHPDU(request);
-        } else if (type == CHUNK_HPDS) {
-            return handleHPDS(request);
-        } else if (type == CHUNK_NHSG) {
-            return handleHPSGNHSG(request, true);
-        } else if (type == CHUNK_HPGC) {
+        if (type == CHUNK_HPGC) {
             return handleHPGC(request);
-        } else if (type == CHUNK_REAE) {
-            return handleREAE(request);
-        } else if (type == CHUNK_REAQ) {
-            return handleREAQ(request);
-        } else if (type == CHUNK_REAL) {
-            return handleREAL(request);
         } else {
             throw new RuntimeException("Unknown packet "
                 + ChunkHandler.name(type));
@@ -106,112 +74,6 @@
     }
 
     /*
-     * Handle a "HeaP InFo" request.
-     */
-    private Chunk handleHPIF(Chunk request) {
-        ByteBuffer in = wrapChunk(request);
-
-        int when = in.get();
-        if (false)
-            Log.v("ddm-heap", "Heap segment enable: when=" + when);
-
-        boolean ok = DdmVmInternal.heapInfoNotify(when);
-        if (!ok) {
-            return createFailChunk(1, "Unsupported HPIF what");
-        } else {
-            return null;        // empty response
-        }
-    }
-
-    /*
-     * Handle a "HeaP SeGment" or "Native Heap SeGment" request.
-     */
-    private Chunk handleHPSGNHSG(Chunk request, boolean isNative) {
-        ByteBuffer in = wrapChunk(request);
-
-        int when = in.get();
-        int what = in.get();
-        if (false)
-            Log.v("ddm-heap", "Heap segment enable: when=" + when
-                + ", what=" + what + ", isNative=" + isNative);
-
-        boolean ok = DdmVmInternal.heapSegmentNotify(when, what, isNative);
-        if (!ok) {
-            return createFailChunk(1, "Unsupported HPSG what/when");
-        } else {
-            // TODO: if "when" is non-zero and we want to see a dump
-            //       right away, initiate a GC.
-            return null;        // empty response
-        }
-    }
-
-    /*
-     * Handle a "HeaP DUmp" request.
-     *
-     * This currently just returns a result code.  We could pull up
-     * the entire contents of the file and return them, but hprof dump
-     * files can be a few megabytes.
-     */
-    private Chunk handleHPDU(Chunk request) {
-        ByteBuffer in = wrapChunk(request);
-        byte result;
-
-        /* get the filename for the output file */
-        int len = in.getInt();
-        String fileName = getString(in, len);
-        if (false)
-            Log.d("ddm-heap", "Heap dump: file='" + fileName + "'");
-
-        try {
-            Debug.dumpHprofData(fileName);
-            result = 0;
-        } catch (UnsupportedOperationException uoe) {
-            Log.w("ddm-heap", "hprof dumps not supported in this VM");
-            result = -1;
-        } catch (IOException ioe) {
-            result = -1;
-        } catch (RuntimeException re) {
-            result = -1;
-        }
-
-        /* create a non-empty reply so the handler fires on completion */
-        byte[] reply = { result };
-        return new Chunk(CHUNK_HPDU, reply, 0, reply.length);
-    }
-
-    /*
-     * Handle a "HeaP Dump Streaming" request.
-     *
-     * This tells the VM to create a heap dump and send it directly to
-     * DDMS.  The dumps are large enough that we don't want to copy the
-     * data into a byte[] and send it from here.
-     */
-    private Chunk handleHPDS(Chunk request) {
-        ByteBuffer in = wrapChunk(request);
-        byte result;
-
-        /* get the filename for the output file */
-        if (false)
-            Log.d("ddm-heap", "Heap dump: [DDMS]");
-
-        String failMsg = null;
-        try {
-            Debug.dumpHprofDataDdms();
-        } catch (UnsupportedOperationException uoe) {
-            failMsg = "hprof dumps not supported in this VM";
-        } catch (RuntimeException re) {
-            failMsg = "Exception: " + re.getMessage();
-        }
-
-        if (failMsg != null) {
-            Log.w("ddm-heap", failMsg);
-            return createFailChunk(1, failMsg);
-        } else {
-            return null;
-        }
-    }
-
-    /*
      * Handle a "HeaP Garbage Collection" request.
      */
     private Chunk handleHPGC(Chunk request) {
@@ -223,47 +85,4 @@
 
         return null;        // empty response
     }
-
-    /*
-     * Handle a "REcent Allocation Enable" request.
-     */
-    private Chunk handleREAE(Chunk request) {
-        ByteBuffer in = wrapChunk(request);
-        boolean enable;
-
-        enable = (in.get() != 0);
-
-        if (false)
-            Log.d("ddm-heap", "Recent allocation enable request: " + enable);
-
-        DdmVmInternal.enableRecentAllocations(enable);
-
-        return null;        // empty response
-    }
-
-    /*
-     * Handle a "REcent Allocation Query" request.
-     */
-    private Chunk handleREAQ(Chunk request) {
-        //ByteBuffer in = wrapChunk(request);
-
-        byte[] reply = new byte[1];
-        reply[0] = DdmVmInternal.getRecentAllocationStatus() ? (byte)1 :(byte)0;
-        return new Chunk(CHUNK_REAQ, reply, 0, reply.length);
-    }
-
-    /*
-     * Handle a "REcent ALlocations" request.
-     */
-    private Chunk handleREAL(Chunk request) {
-        //ByteBuffer in = wrapChunk(request);
-
-        if (false)
-            Log.d("ddm-heap", "Recent allocations request");
-
-        /* generate the reply in a ready-to-go format */
-        byte[] reply = DdmVmInternal.getRecentAllocations();
-        return new Chunk(CHUNK_REAL, reply, 0, reply.length);
-    }
 }
-
diff --git a/core/java/android/ddm/DdmHandleThread.java b/core/java/android/ddm/DdmHandleThread.java
deleted file mode 100644
index 613ab75..0000000
--- a/core/java/android/ddm/DdmHandleThread.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2007 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.ddm;
-
-import org.apache.harmony.dalvik.ddmc.Chunk;
-import org.apache.harmony.dalvik.ddmc.ChunkHandler;
-import org.apache.harmony.dalvik.ddmc.DdmServer;
-import org.apache.harmony.dalvik.ddmc.DdmVmInternal;
-import android.util.Log;
-import java.nio.ByteBuffer;
-
-/**
- * Handle thread-related traffic.
- */
-public class DdmHandleThread extends ChunkHandler {
-
-    public static final int CHUNK_THEN = type("THEN");
-    public static final int CHUNK_THCR = type("THCR");
-    public static final int CHUNK_THDE = type("THDE");
-    public static final int CHUNK_THST = type("THST");
-    public static final int CHUNK_STKL = type("STKL");
-
-    private static DdmHandleThread mInstance = new DdmHandleThread();
-
-
-    /* singleton, do not instantiate */
-    private DdmHandleThread() {}
-
-    /**
-     * Register for the messages we're interested in.
-     */
-    public static void register() {
-        DdmServer.registerHandler(CHUNK_THEN, mInstance);
-        DdmServer.registerHandler(CHUNK_THST, mInstance);
-        DdmServer.registerHandler(CHUNK_STKL, mInstance);
-    }
-
-    /**
-     * Called when the DDM server connects.  The handler is allowed to
-     * send messages to the server.
-     */
-    public void connected() {}
-
-    /**
-     * Called when the DDM server disconnects.  Can be used to disable
-     * periodic transmissions or clean up saved state.
-     */
-    public void disconnected() {}
-
-    /**
-     * Handle a chunk of data.
-     */
-    public Chunk handleChunk(Chunk request) {
-        if (false)
-            Log.v("ddm-thread", "Handling " + name(request.type) + " chunk");
-        int type = request.type;
-
-        if (type == CHUNK_THEN) {
-            return handleTHEN(request);
-        } else if (type == CHUNK_THST) {
-            return handleTHST(request);
-        } else if (type == CHUNK_STKL) {
-            return handleSTKL(request);
-        } else {
-            throw new RuntimeException("Unknown packet "
-                + ChunkHandler.name(type));
-        }
-    }
-
-    /*
-     * Handle a "THread notification ENable" request.
-     */
-    private Chunk handleTHEN(Chunk request) {
-        ByteBuffer in = wrapChunk(request);
-
-        boolean enable = (in.get() != 0);
-        //Log.i("ddm-thread", "Thread notify enable: " + enable);
-
-        DdmVmInternal.threadNotify(enable);
-        return null;        // empty response
-    }
-
-    /*
-     * Handle a "THread STatus" request.  This is constructed by the VM.
-     */
-    private Chunk handleTHST(Chunk request) {
-        ByteBuffer in = wrapChunk(request);
-        // currently nothing to read from "in"
-
-        //Log.d("ddm-thread", "Thread status request");
-
-        byte[] status = DdmVmInternal.getThreadStats();
-        if (status != null)
-            return new Chunk(CHUNK_THST, status, 0, status.length);
-        else
-            return createFailChunk(1, "Can't build THST chunk");
-    }
-
-    /*
-     * Handle a STacK List request.
-     *
-     * This is done by threadId, which isn't great since those are
-     * recycled.  We need a thread serial ID.  The Linux tid is an okay
-     * answer as it's unlikely to recycle at the exact wrong moment.
-     * However, we're using the short threadId in THST messages, so we
-     * use them here for consistency.  (One thought is to keep the current
-     * thread ID in the low 16 bits and somehow serialize the top 16 bits.)
-     */
-    private Chunk handleSTKL(Chunk request) {
-        ByteBuffer in = wrapChunk(request);
-        int threadId;
-
-        threadId = in.getInt();
-
-        //Log.d("ddm-thread", "Stack list request " + threadId);
-
-        StackTraceElement[] trace = DdmVmInternal.getStackTraceById(threadId);
-        if (trace == null) {
-            return createFailChunk(1, "Stack trace unavailable");
-        } else {
-            return createStackChunk(trace, threadId);
-        }
-    }
-
-    /*
-     * Serialize a StackTraceElement[] into an STKL chunk.
-     *
-     * We include the threadId in the response so the other side doesn't have
-     * to match up requests and responses as carefully.
-     */
-    private Chunk createStackChunk(StackTraceElement[] trace, int threadId) {
-        int bufferSize = 0;
-
-        bufferSize += 4;            // version, flags, whatever
-        bufferSize += 4;            // thread ID
-        bufferSize += 4;            // frame count
-        for (StackTraceElement elem : trace) {
-            bufferSize += 4 + elem.getClassName().length() * 2;
-            bufferSize += 4 + elem.getMethodName().length() * 2;
-            bufferSize += 4;
-            if (elem.getFileName() != null)
-                bufferSize += elem.getFileName().length() * 2;
-            bufferSize += 4;        // line number
-        }
-
-        ByteBuffer out = ByteBuffer.allocate(bufferSize);
-        out.putInt(0);
-        out.putInt(threadId);
-        out.putInt(trace.length);
-        for (StackTraceElement elem : trace) {
-            out.putInt(elem.getClassName().length());
-            putString(out, elem.getClassName());
-            out.putInt(elem.getMethodName().length());
-            putString(out, elem.getMethodName());
-            if (elem.getFileName() != null) {
-                out.putInt(elem.getFileName().length());
-                putString(out, elem.getFileName());
-            } else {
-                out.putInt(0);
-            }
-            out.putInt(elem.getLineNumber());
-        }
-
-        return new Chunk(CHUNK_STKL, out);
-    }
-}
-
diff --git a/core/java/android/ddm/DdmRegister.java b/core/java/android/ddm/DdmRegister.java
index e0faa51..ca10312 100644
--- a/core/java/android/ddm/DdmRegister.java
+++ b/core/java/android/ddm/DdmRegister.java
@@ -16,9 +16,10 @@
 
 package android.ddm;
 
-import org.apache.harmony.dalvik.ddmc.DdmServer;
 import android.util.Log;
 
+import org.apache.harmony.dalvik.ddmc.DdmServer;
+
 /**
  * Just a place to stick handler registrations, instead of scattering
  * them around.
@@ -46,7 +47,6 @@
         if (false)
             Log.v("ddm", "Registering DDM message handlers");
         DdmHandleHello.register();
-        DdmHandleThread.register();
         DdmHandleHeap.register();
         DdmHandleNativeHeap.register();
         DdmHandleProfiling.register();
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 7f834af..933dee3 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -2024,7 +2024,9 @@
                 // Tell listeners that the cameras and torch modes are unavailable and schedule a
                 // reconnection to camera service. When camera service is reconnected, the camera
                 // and torch statuses will be updated.
-                for (int i = 0; i < mDeviceStatus.size(); i++) {
+                // Iterate from the end to the beginning befcause onStatusChangedLocked removes
+                // entries from the ArrayMap.
+                for (int i = mDeviceStatus.size() - 1; i >= 0; i--) {
                     String cameraId = mDeviceStatus.keyAt(i);
                     onStatusChangedLocked(ICameraServiceListener.STATUS_NOT_PRESENT, cameraId);
                 }
diff --git a/core/java/android/net/EthernetNetworkSpecifier.java b/core/java/android/net/EthernetNetworkSpecifier.java
new file mode 100644
index 0000000..e168588
--- /dev/null
+++ b/core/java/android/net/EthernetNetworkSpecifier.java
@@ -0,0 +1,97 @@
+/*
+ * 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.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * A {@link NetworkSpecifier} used to identify ethernet interfaces.
+ *
+ * @see EthernetManager
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class EthernetNetworkSpecifier extends NetworkSpecifier implements Parcelable {
+
+    /**
+     * Name of the network interface.
+     */
+    @NonNull
+    private final String mInterfaceName;
+
+    public EthernetNetworkSpecifier(@NonNull String interfaceName) {
+        Preconditions.checkStringNotEmpty(interfaceName);
+        mInterfaceName = interfaceName;
+    }
+
+    // This may be null in the future to support specifiers based on data other than the interface
+    // name.
+    @Nullable
+    public String getInterfaceName() {
+        return mInterfaceName;
+    }
+
+    @Override
+    public boolean canBeSatisfiedBy(@Nullable NetworkSpecifier other) {
+        return equals(other);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (!(o instanceof EthernetNetworkSpecifier)) return false;
+        return TextUtils.equals(mInterfaceName, ((EthernetNetworkSpecifier) o).mInterfaceName);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(mInterfaceName);
+    }
+
+    @Override
+    public String toString() {
+        return "EthernetNetworkSpecifier (" + mInterfaceName + ")";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mInterfaceName);
+    }
+
+    public static final @NonNull Parcelable.Creator<EthernetNetworkSpecifier> CREATOR =
+            new Parcelable.Creator<EthernetNetworkSpecifier>() {
+        public EthernetNetworkSpecifier createFromParcel(Parcel in) {
+            return new EthernetNetworkSpecifier(in.readString());
+        }
+        public EthernetNetworkSpecifier[] newArray(int size) {
+            return new EthernetNetworkSpecifier[size];
+        }
+    };
+}
diff --git a/core/java/android/net/INetworkPolicyListener.aidl b/core/java/android/net/INetworkPolicyListener.aidl
index dfb1e99..00c6913 100644
--- a/core/java/android/net/INetworkPolicyListener.aidl
+++ b/core/java/android/net/INetworkPolicyListener.aidl
@@ -25,4 +25,5 @@
     void onUidPoliciesChanged(int uid, int uidPolicies);
     void onSubscriptionOverride(int subId, int overrideMask, int overrideValue, in int[] networkTypes);
     void onSubscriptionPlansChanged(int subId, in SubscriptionPlan[] plans);
+    void onBlockedReasonChanged(int uid, int oldBlockedReason, int newBlockedReason);
 }
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 9bf791b..171c6a2 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -62,6 +62,7 @@
         3 - enabled
     */
     int getRestrictBackgroundByCaller();
+    int getRestrictBackgroundStatus(int uid);
 
     void setDeviceIdleMode(boolean enabled);
     void setWifiMeteredOverride(String networkId, int meteredOverride);
diff --git a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl b/core/java/android/net/IPacProxyInstalledListener.aidl
similarity index 70%
copy from packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
copy to core/java/android/net/IPacProxyInstalledListener.aidl
index 7979afc..b1f946e 100644
--- a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
+++ b/core/java/android/net/IPacProxyInstalledListener.aidl
@@ -1,12 +1,11 @@
-/**
- *
+/*
  * 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
+ *      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,
@@ -17,7 +16,10 @@
 
 package android.net;
 
-/** @hide */
-oneway interface IOnSetOemNetworkPreferenceListener {
-    void onComplete();
+import android.net.Network;
+import android.net.ProxyInfo;
+
+/** {@hide} */
+oneway interface IPacProxyInstalledListener {
+    void onPacProxyInstalled(in Network network, in ProxyInfo proxy);
 }
diff --git a/core/java/android/net/IPacProxyManager.aidl b/core/java/android/net/IPacProxyManager.aidl
new file mode 100644
index 0000000..8f65c56
--- /dev/null
+++ b/core/java/android/net/IPacProxyManager.aidl
@@ -0,0 +1,28 @@
+/**
+ * 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 perNmissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.IPacProxyInstalledListener;
+import android.net.ProxyInfo;
+
+/** {@hide} */
+interface IPacProxyManager
+{
+    void addListener(IPacProxyInstalledListener listener);
+    void removeListener(IPacProxyInstalledListener listener);
+    void setCurrentProxyScriptUrl(in ProxyInfo proxyInfo);
+}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 1c56954..3971329 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -18,11 +18,14 @@
 
 import static android.app.ActivityManager.procStateToString;
 import static android.content.pm.PackageManager.GET_SIGNATURES;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_MASK;
+import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.app.ActivityManager;
@@ -44,6 +47,8 @@
 import android.util.Pair;
 import android.util.Range;
 
+import com.android.internal.util.function.pooled.PooledLambda;
+
 import com.google.android.collect.Sets;
 
 import java.lang.annotation.Retention;
@@ -53,6 +58,7 @@
 import java.util.Iterator;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
 
 /**
  * Manager for creating and modifying network policy rules.
@@ -60,6 +66,7 @@
  * @hide
  */
 @TestApi
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 @SystemService(Context.NETWORK_POLICY_SERVICE)
 public class NetworkPolicyManager {
 
@@ -198,12 +205,80 @@
     })
     public @interface SubscriptionOverrideMask {}
 
+    /**
+     * Flag to indicate that app is not exempt from any network restrictions.
+     *
+     * @hide
+     */
+    public static final int ALLOWED_REASON_NONE = 0;
+    /**
+     * Flag to indicate that app is exempt from certain network restrictions because of it being a
+     * system component.
+     *
+     * @hide
+     */
+    public static final int ALLOWED_REASON_SYSTEM = 1 << 0;
+    /**
+     * Flag to indicate that app is exempt from certain network restrictions because of it being
+     * in the foreground.
+     *
+     * @hide
+     */
+    public static final int ALLOWED_REASON_FOREGROUND = 1 << 1;
+    /**
+     * Flag to indicate that app is exempt from certain network restrictions because of it being
+     * in the {@code allow-in-power-save} list.
+     *
+     * @hide
+     */
+    public static final int ALLOWED_REASON_POWER_SAVE_ALLOWLIST = 1 << 2;
+    /**
+     * Flag to indicate that app is exempt from certain network restrictions because of it being
+     * in the {@code allow-in-power-save-except-idle} list.
+     *
+     * @hide
+     */
+    public static final int ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST = 1 << 3;
+    /**
+     * Flag to indicate that app is exempt from certain network restrictions because of it holding
+     * certain privileged permissions.
+     *
+     * @hide
+     */
+    public static final int ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS = 1 << 4;
+    /**
+     * Flag to indicate that app is exempt from certain metered network restrictions because user
+     * explicitly exempted it.
+     *
+     * @hide
+     */
+    public static final int ALLOWED_METERED_REASON_USER_EXEMPTED = 1 << 16;
+    /**
+     * Flag to indicate that app is exempt from certain metered network restrictions because of it
+     * being a system component.
+     *
+     * @hide
+     */
+    public static final int ALLOWED_METERED_REASON_SYSTEM = 1 << 17;
+    /**
+     * Flag to indicate that app is exempt from certain metered network restrictions because of it
+     * being in the foreground.
+     *
+     * @hide
+     */
+    public static final int ALLOWED_METERED_REASON_FOREGROUND = 1 << 18;
+
+    /** @hide */
+    public static final int ALLOWED_METERED_REASON_MASK = 0xffff0000;
+
     private final Context mContext;
     @UnsupportedAppUsage
     private INetworkPolicyManager mService;
 
     private final Map<SubscriptionCallback, SubscriptionCallbackProxy>
-            mCallbackMap = new ConcurrentHashMap<>();
+            mSubscriptionCallbackMap = new ConcurrentHashMap<>();
+    private final Map<NetworkPolicyCallback, NetworkPolicyCallbackProxy>
+            mNetworkPolicyCallbackMap = new ConcurrentHashMap<>();
 
     /** @hide */
     public NetworkPolicyManager(Context context, INetworkPolicyManager service) {
@@ -318,7 +393,7 @@
         }
 
         final SubscriptionCallbackProxy callbackProxy = new SubscriptionCallbackProxy(callback);
-        if (null != mCallbackMap.putIfAbsent(callback, callbackProxy)) {
+        if (null != mSubscriptionCallbackMap.putIfAbsent(callback, callbackProxy)) {
             throw new IllegalArgumentException("Callback is already registered.");
         }
         registerListener(callbackProxy);
@@ -331,7 +406,7 @@
             throw new NullPointerException("Callback cannot be null.");
         }
 
-        final SubscriptionCallbackProxy callbackProxy = mCallbackMap.remove(callback);
+        final SubscriptionCallbackProxy callbackProxy = mSubscriptionCallbackMap.remove(callback);
         if (callbackProxy == null) return;
 
         unregisterListener(callbackProxy);
@@ -379,6 +454,26 @@
     }
 
     /**
+     * Determines if an UID is subject to metered network restrictions while running in background.
+     *
+     * @param uid The UID whose status needs to be checked.
+     * @return {@link ConnectivityManager#RESTRICT_BACKGROUND_STATUS_DISABLED},
+     *         {@link ConnectivityManager##RESTRICT_BACKGROUND_STATUS_ENABLED},
+     *         or {@link ConnectivityManager##RESTRICT_BACKGROUND_STATUS_WHITELISTED} to denote
+     *         the current status of the UID.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
+    public int getRestrictBackgroundStatus(int uid) {
+        try {
+            return mService.getRestrictBackgroundStatus(uid);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Override connections to be temporarily marked as either unmetered or congested,
      * along with automatic timeouts if desired.
      *
@@ -460,9 +555,8 @@
      * @param meteredNetwork True if the network is metered.
      * @return true if networking is blocked for the given uid according to current networking
      *         policies.
-     *
-     * @hide
      */
+    @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY)
     public boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork) {
         try {
             return mService.isUidNetworkingBlocked(uid, meteredNetwork);
@@ -501,9 +595,8 @@
      *
      * @param uid The target uid.
      * @return true if the given uid is restricted from doing networking on metered networks.
-     *
-     * @hide
      */
+    @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY)
     public boolean isUidRestrictedOnMeteredNetworks(int uid) {
         try {
             return mService.isUidRestrictedOnMeteredNetworks(uid);
@@ -513,11 +606,15 @@
     }
 
     /**
-     * Get multipath preference for the given network.
+     * Gets a hint on whether it is desirable to use multipath data transfer on the given network.
+     *
+     * @return One of the ConnectivityManager.MULTIPATH_PREFERENCE_* constants.
      *
      * @hide
      */
-    public int getMultipathPreference(Network network) {
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
+    public int getMultipathPreference(@NonNull Network network) {
         try {
             return mService.getMultipathPreference(network);
         } catch (RemoteException e) {
@@ -689,6 +786,142 @@
         return WifiInfo.sanitizeSsid(ssid);
     }
 
+    /**
+     * Returns whether network access of an UID is blocked or not based on {@code blockedReasons}
+     * corresponding to it.
+     *
+     * {@code blockedReasons} would be a bitwise {@code OR} combination of the
+     * {@code BLOCKED_REASON_*} and/or {@code BLOCKED_METERED_REASON_*} constants.
+     *
+     * @param blockedReasons Value indicating the reasons for why the network access of an UID is
+     *                       blocked. If the value is equal to
+     *                       {@link ConnectivityManager#BLOCKED_REASON_NONE}, then
+     *                       it indicates that an app's network access is not blocked.
+     * @param meteredNetwork Value indicating whether the network is metered or not.
+     * @return Whether network access is blocked or not.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static boolean isUidBlocked(int blockedReasons, boolean meteredNetwork) {
+        if (blockedReasons == BLOCKED_REASON_NONE) {
+            return false;
+        }
+        final int blockedOnAllNetworksReason = (blockedReasons & ~BLOCKED_METERED_REASON_MASK);
+        if (blockedOnAllNetworksReason != BLOCKED_REASON_NONE) {
+            return true;
+        }
+        if (meteredNetwork) {
+            return blockedReasons != BLOCKED_REASON_NONE;
+        }
+        return false;
+    }
+
+    /**
+     * Returns the {@code string} representation of {@code blockedReasons} argument.
+     *
+     * @param blockedReasons Value indicating the reasons for why the network access of an UID is
+     *                       blocked.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @NonNull
+    public static String blockedReasonsToString(int blockedReasons) {
+        return DebugUtils.flagsToString(NetworkPolicyManager.class, "BLOCKED_", blockedReasons);
+    }
+
+    /**
+     * Register a {@link NetworkPolicyCallback} to listen for changes to network blocked status
+     * of apps.
+     *
+     * Note that when a caller tries to register a new callback, it might replace a previously
+     * registered callback if it is considered equal to the new one, based on the
+     * {@link Object#equals(Object)} check.
+     *
+     * @param executor The {@link Executor} to run the callback on.
+     * @param callback The {@link NetworkPolicyCallback} to be registered.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY)
+    public void registerNetworkPolicyCallback(@Nullable Executor executor,
+            @NonNull NetworkPolicyCallback callback) {
+        if (callback == null) {
+            throw new NullPointerException("Callback cannot be null.");
+        }
+
+        final NetworkPolicyCallbackProxy callbackProxy = new NetworkPolicyCallbackProxy(
+                executor, callback);
+        registerListener(callbackProxy);
+        mNetworkPolicyCallbackMap.put(callback, callbackProxy);
+    }
+
+    /**
+     * Unregister a previously registered {@link NetworkPolicyCallback}.
+     *
+     * @param callback The {@link NetworkPolicyCallback} to be unregistered.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY)
+    public void unregisterNetworkPolicyCallback(@NonNull NetworkPolicyCallback callback) {
+        if (callback == null) {
+            throw new NullPointerException("Callback cannot be null.");
+        }
+
+        final NetworkPolicyCallbackProxy callbackProxy = mNetworkPolicyCallbackMap.remove(callback);
+        if (callbackProxy == null) return;
+        unregisterListener(callbackProxy);
+    }
+
+    /**
+     * Interface for the callback to listen for changes to network blocked status of apps.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public interface NetworkPolicyCallback {
+        /**
+         * Called when the reason for why the network access of an UID is blocked changes.
+         *
+         * @param uid The UID for which the blocked status changed.
+         * @param blockedReasons Value indicating the reasons for why the network access of an
+         *                       UID is blocked.
+         * @hide
+         */
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        default void onUidBlockedReasonChanged(int uid, int blockedReasons) {}
+    }
+
+    /** @hide */
+    public static class NetworkPolicyCallbackProxy extends Listener {
+        private final Executor mExecutor;
+        private final NetworkPolicyCallback mCallback;
+
+        NetworkPolicyCallbackProxy(@Nullable Executor executor,
+                @NonNull NetworkPolicyCallback callback) {
+            mExecutor = executor;
+            mCallback = callback;
+        }
+
+        @Override
+        public void onBlockedReasonChanged(int uid, int oldBlockedReasons, int newBlockedReasons) {
+            if (oldBlockedReasons != newBlockedReasons) {
+                dispatchOnUidBlockedReasonChanged(mExecutor, mCallback, uid, newBlockedReasons);
+            }
+        }
+    }
+
+    private static void dispatchOnUidBlockedReasonChanged(@Nullable Executor executor,
+            @NonNull NetworkPolicyCallback callback, int uid, int blockedReasons) {
+        if (executor == null) {
+            callback.onUidBlockedReasonChanged(uid, blockedReasons);
+        } else {
+            executor.execute(PooledLambda.obtainRunnable(
+                    NetworkPolicyCallback::onUidBlockedReasonChanged,
+                    callback, uid, blockedReasons).recycleOnUse());
+        }
+    }
+
     /** @hide */
     public static class SubscriptionCallback {
         /**
@@ -743,5 +976,7 @@
         @Override public void onSubscriptionOverride(int subId, int overrideMask,
                 int overrideValue, int[] networkTypes) { }
         @Override public void onSubscriptionPlansChanged(int subId, SubscriptionPlan[] plans) { }
+        @Override public void onBlockedReasonChanged(int uid,
+                int oldBlockedReasons, int newBlockedReasons) { }
     }
 }
diff --git a/core/java/android/net/PacProxyManager.java b/core/java/android/net/PacProxyManager.java
new file mode 100644
index 0000000..8f7ad8c
--- /dev/null
+++ b/core/java/android/net/PacProxyManager.java
@@ -0,0 +1,150 @@
+/*
+ * 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.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteException;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+@SystemService(Context.PAC_PROXY_SERVICE)
+public class PacProxyManager {
+    private final Context mContext;
+    private final IPacProxyManager mService;
+    @GuardedBy("mListenerMap")
+    private final HashMap<PacProxyInstalledListener, PacProxyInstalledListenerProxy>
+            mListenerMap = new HashMap<>();
+
+    /** @hide */
+    public PacProxyManager(Context context, IPacProxyManager service) {
+        Objects.requireNonNull(service, "missing IPacProxyManager");
+        mContext = context;
+        mService = service;
+    }
+
+    /**
+     * Add a listener to start monitoring events reported by PacProxyService.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_STACK,
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_SETTINGS})
+    public void addPacProxyInstalledListener(@NonNull Executor executor,
+            @NonNull PacProxyInstalledListener listener) {
+        try {
+            synchronized (mListenerMap) {
+                final PacProxyInstalledListenerProxy listenerProxy =
+                        new PacProxyInstalledListenerProxy(executor, listener);
+
+                if (null != mListenerMap.putIfAbsent(listener, listenerProxy)) {
+                    throw new IllegalStateException("Listener is already added.");
+                }
+                mService.addListener(listenerProxy);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Remove the listener to stop monitoring the event of PacProxyInstalledListener.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_STACK,
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_SETTINGS})
+    public void removePacProxyInstalledListener(@NonNull PacProxyInstalledListener listener) {
+        try {
+            synchronized (mListenerMap) {
+                final PacProxyInstalledListenerProxy listenerProxy = mListenerMap.remove(listener);
+                if (listenerProxy == null) return;
+                mService.removeListener(listenerProxy);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Updates the PAC Proxy Installer with current Proxy information.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_STACK,
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_SETTINGS})
+    public void setCurrentProxyScriptUrl(@Nullable ProxyInfo proxy) {
+        try {
+            mService.setCurrentProxyScriptUrl(proxy);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * A callback interface for monitoring changes of PAC proxy information.
+     */
+    public interface PacProxyInstalledListener {
+        /**
+         * Notify that the PAC proxy has been installed. Note that this method will be called with
+         * a ProxyInfo with an empty PAC URL when the PAC proxy is removed.
+         *
+         * This method supports different PAC proxies per-network but not all devices might support
+         * per-network proxies. In that case it will be applied globally.
+         *
+         * @param network the network for which this proxy installed.
+         * @param proxy the installed proxy.
+         */
+        void onPacProxyInstalled(@Nullable Network network, @NonNull ProxyInfo proxy);
+    }
+
+    /**
+     * PacProxyInstalledListener proxy for PacProxyInstalledListener object.
+     * @hide
+     */
+    public class PacProxyInstalledListenerProxy extends IPacProxyInstalledListener.Stub {
+        private final Executor mExecutor;
+        private final PacProxyInstalledListener mListener;
+
+        PacProxyInstalledListenerProxy(Executor executor, PacProxyInstalledListener listener) {
+            mExecutor = executor;
+            mListener = listener;
+        }
+
+        @Override
+        public void onPacProxyInstalled(Network network, ProxyInfo proxy) {
+            Binder.withCleanCallingIdentity(() -> {
+                mExecutor.execute(() -> {
+                    mListener.onPacProxyInstalled(network, proxy);
+                });
+            });
+        }
+    }
+}
diff --git a/core/java/android/net/PacProxySelector.java b/core/java/android/net/PacProxySelector.java
index 326943a..84b7eec 100644
--- a/core/java/android/net/PacProxySelector.java
+++ b/core/java/android/net/PacProxySelector.java
@@ -51,7 +51,7 @@
                 ServiceManager.getService(PROXY_SERVICE));
         if (mProxyService == null) {
             // Added because of b10267814 where mako is restarting.
-            Log.e(TAG, "PacProxyInstaller: no proxy service");
+            Log.e(TAG, "PacProxyService: no proxy service");
         }
         mDefaultList = Lists.newArrayList(java.net.Proxy.NO_PROXY);
     }
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index f472ed4..5f65d46 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -16,12 +16,15 @@
 
 package android.net;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
 import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.content.ComponentName;
@@ -56,18 +59,21 @@
  */
 public class VpnManager {
     /** Type representing a lack of VPN @hide */
+    @SystemApi(client = MODULE_LIBRARIES)
     public static final int TYPE_VPN_NONE = -1;
 
     /**
      * A VPN created by an app using the {@link VpnService} API.
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public static final int TYPE_VPN_SERVICE = 1;
 
     /**
      * A VPN created using a {@link VpnManager} API such as {@link #startProvisionedVpnProfile}.
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public static final int TYPE_VPN_PLATFORM = 2;
 
     /**
@@ -76,16 +82,25 @@
      * @hide
      */
     @Deprecated
+    @SystemApi(client = MODULE_LIBRARIES)
     public static final int TYPE_VPN_LEGACY = 3;
 
     /**
+     * An VPN created by OEM code through other means than {@link VpnService} or {@link VpnManager}.
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int TYPE_VPN_OEM = 4;
+
+    /**
      * Channel for VPN notifications.
      * @hide
      */
     public static final String NOTIFICATION_CHANNEL_VPN = "VPN";
 
     /** @hide */
-    @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM, TYPE_VPN_LEGACY})
+    @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM, TYPE_VPN_LEGACY,
+            TYPE_VPN_OEM})
     @Retention(RetentionPolicy.SOURCE)
     public @interface VpnType {}
 
diff --git a/core/java/android/net/vcn/IVcnManagementService.aidl b/core/java/android/net/vcn/IVcnManagementService.aidl
index 6a3cb42..5b79f73 100644
--- a/core/java/android/net/vcn/IVcnManagementService.aidl
+++ b/core/java/android/net/vcn/IVcnManagementService.aidl
@@ -29,7 +29,7 @@
  */
 interface IVcnManagementService {
     void setVcnConfig(in ParcelUuid subscriptionGroup, in VcnConfig config, in String opPkgName);
-    void clearVcnConfig(in ParcelUuid subscriptionGroup);
+    void clearVcnConfig(in ParcelUuid subscriptionGroup, in String opPkgName);
 
     void addVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener);
     void removeVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener);
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 9f83b21..d4e8e2d 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -52,13 +52,12 @@
  * Network}s.
  *
  * <p>A VCN connection based on this configuration will be brought up dynamically based on device
- * settings, and filed NetworkRequests. Underlying networks will be selected based on the services
- * required by this configuration (as represented by network capabilities), and must be part of the
- * subscription group under which this configuration is registered (see {@link
+ * settings, and filed NetworkRequests. Underlying Networks must provide INTERNET connectivity, and
+ * must be part of the subscription group under which this configuration is registered (see {@link
  * VcnManager#setVcnConfig}).
  *
- * <p>As an abstraction of a cellular network, services that can be provided by a VCN network, or
- * required for underlying networks are limited to services provided by cellular networks:
+ * <p>As an abstraction of a cellular network, services that can be provided by a VCN network are
+ * limited to services provided by cellular networks:
  *
  * <ul>
  *   <li>{@link NetworkCapabilities#NET_CAPABILITY_MMS}
@@ -214,13 +213,6 @@
             checkValidCapability(cap);
         }
 
-        Preconditions.checkArgument(
-                mUnderlyingCapabilities != null && !mUnderlyingCapabilities.isEmpty(),
-                "underlyingCapabilities was null or empty");
-        for (Integer cap : getAllUnderlyingCapabilities()) {
-            checkValidCapability(cap);
-        }
-
         Objects.requireNonNull(mRetryIntervalsMs, "retryIntervalsMs was null");
         validateRetryInterval(mRetryIntervalsMs);
 
@@ -295,7 +287,9 @@
      *
      * @see Builder#addRequiredUnderlyingCapability(int)
      * @see Builder#removeRequiredUnderlyingCapability(int)
+     * @hide
      */
+    // TODO(b/182219992): Remove, and add when per-transport capabilities are supported
     @NonNull
     public int[] getRequiredUnderlyingCapabilities() {
         // Sorted set guarantees ordering
@@ -470,7 +464,9 @@
          * @return this {@link Builder} instance, for chaining
          * @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying
          *     networks
+         * @hide
          */
+        // TODO(b/182219992): Remove, and add when per-transport capabilities are supported
         @NonNull
         public Builder addRequiredUnderlyingCapability(
                 @VcnSupportedCapability int underlyingCapability) {
@@ -492,7 +488,9 @@
          * @return this {@link Builder} instance, for chaining
          * @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying
          *     networks
+         * @hide
          */
+        // TODO(b/182219992): Remove, and add when per-transport capabilities are supported
         @NonNull
         @SuppressLint("BuilderSetStyle") // For consistency with NetCaps.Builder add/removeCap
         public Builder removeRequiredUnderlyingCapability(
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 8ebf757..abd41da 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -73,7 +73,8 @@
 public class VcnManager {
     @NonNull private static final String TAG = VcnManager.class.getSimpleName();
 
-    private static final Map<VcnNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
+    private static final Map<
+                    VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder>
             REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>();
 
     @NonNull private final Context mContext;
@@ -93,13 +94,13 @@
     }
 
     /**
-     * Get all currently registered VcnNetworkPolicyListeners for testing purposes.
+     * Get all currently registered VcnNetworkPolicyChangeListeners for testing purposes.
      *
      * @hide
      */
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     @NonNull
-    public static Map<VcnNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
+    public static Map<VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder>
             getAllPolicyListeners() {
         return Collections.unmodifiableMap(REGISTERED_POLICY_LISTENERS);
     }
@@ -153,7 +154,7 @@
         requireNonNull(subscriptionGroup, "subscriptionGroup was null");
 
         try {
-            mService.clearVcnConfig(subscriptionGroup);
+            mService.clearVcnConfig(subscriptionGroup, mContext.getOpPackageName());
         } catch (ServiceSpecificException e) {
             throw new IOException(e);
         } catch (RemoteException e) {
@@ -162,14 +163,14 @@
     }
 
     // TODO(b/180537630): remove all VcnUnderlyingNetworkPolicyListener refs once Telephony is using
-    // the new VcnNetworkPolicyListener API
+    // the new VcnNetworkPolicyChangeListener API
     /**
      * VcnUnderlyingNetworkPolicyListener is the interface through which internal system components
      * can register to receive updates for VCN-underlying Network policies from the System Server.
      *
      * @hide
      */
-    public interface VcnUnderlyingNetworkPolicyListener extends VcnNetworkPolicyListener {}
+    public interface VcnUnderlyingNetworkPolicyListener extends VcnNetworkPolicyChangeListener {}
 
     /**
      * Add a listener for VCN-underlying network policy updates.
@@ -185,7 +186,7 @@
     @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
     public void addVcnUnderlyingNetworkPolicyListener(
             @NonNull Executor executor, @NonNull VcnUnderlyingNetworkPolicyListener listener) {
-        addVcnNetworkPolicyListener(executor, listener);
+        addVcnNetworkPolicyChangeListener(executor, listener);
     }
 
     /**
@@ -198,7 +199,7 @@
      */
     public void removeVcnUnderlyingNetworkPolicyListener(
             @NonNull VcnUnderlyingNetworkPolicyListener listener) {
-        removeVcnNetworkPolicyListener(listener);
+        removeVcnNetworkPolicyChangeListener(listener);
     }
 
     /**
@@ -233,20 +234,20 @@
     }
 
     /**
-     * VcnNetworkPolicyListener is the interface through which internal system components (e.g.
-     * Network Factories) can register to receive updates for VCN-underlying Network policies from
-     * the System Server.
+     * VcnNetworkPolicyChangeListener is the interface through which internal system components
+     * (e.g. Network Factories) can register to receive updates for VCN-underlying Network policies
+     * from the System Server.
      *
      * <p>Any Network Factory that brings up Networks capable of being VCN-underlying Networks
-     * should register a VcnNetworkPolicyListener. VcnManager will then use this listener to notify
-     * the registrant when VCN Network policies change. Upon receiving this signal, the listener
-     * must check {@link VcnManager} for the current Network policy result for each of its Networks
-     * via {@link #applyVcnNetworkPolicy(NetworkCapabilities, LinkProperties)}.
+     * should register a VcnNetworkPolicyChangeListener. VcnManager will then use this listener to
+     * notify the registrant when VCN Network policies change. Upon receiving this signal, the
+     * listener must check {@link VcnManager} for the current Network policy result for each of its
+     * Networks via {@link #applyVcnNetworkPolicy(NetworkCapabilities, LinkProperties)}.
      *
      * @hide
      */
     @SystemApi
-    public interface VcnNetworkPolicyListener {
+    public interface VcnNetworkPolicyChangeListener {
         /**
          * Notifies the implementation that the VCN's underlying Network policy has changed.
          *
@@ -260,20 +261,21 @@
     /**
      * Add a listener for VCN-underlying Network policy updates.
      *
-     * <p>A {@link VcnNetworkPolicyListener} is eligible to begin receiving callbacks once it is
-     * registered. No callbacks are guaranteed upon registration.
+     * <p>A {@link VcnNetworkPolicyChangeListener} is eligible to begin receiving callbacks once it
+     * is registered. No callbacks are guaranteed upon registration.
      *
      * @param executor the Executor that will be used for invoking all calls to the specified
      *     Listener
-     * @param listener the VcnNetworkPolicyListener to be added
+     * @param listener the VcnNetworkPolicyChangeListener to be added
      * @throws SecurityException if the caller does not have permission NETWORK_FACTORY
-     * @throws IllegalStateException if the specified VcnNetworkPolicyListener is already registered
+     * @throws IllegalStateException if the specified VcnNetworkPolicyChangeListener is already
+     *     registered
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
-    public void addVcnNetworkPolicyListener(
-            @NonNull Executor executor, @NonNull VcnNetworkPolicyListener listener) {
+    public void addVcnNetworkPolicyChangeListener(
+            @NonNull Executor executor, @NonNull VcnNetworkPolicyChangeListener listener) {
         requireNonNull(executor, "executor must not be null");
         requireNonNull(listener, "listener must not be null");
 
@@ -292,15 +294,18 @@
     }
 
     /**
-     * Remove the specified VcnNetworkPolicyListener from VcnManager.
+     * Remove the specified VcnNetworkPolicyChangeListener from VcnManager.
      *
      * <p>If the specified listener is not currently registered, this is a no-op.
      *
-     * @param listener the VcnNetworkPolicyListener that will be removed
+     * @param listener the VcnNetworkPolicyChangeListener that will be removed
+     * @throws SecurityException if the caller does not have permission NETWORK_FACTORY
      * @hide
      */
     @SystemApi
-    public void removeVcnNetworkPolicyListener(@NonNull VcnNetworkPolicyListener listener) {
+    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+    public void removeVcnNetworkPolicyChangeListener(
+            @NonNull VcnNetworkPolicyChangeListener listener) {
         requireNonNull(listener, "listener must not be null");
 
         VcnUnderlyingNetworkPolicyListenerBinder binder =
@@ -320,8 +325,9 @@
      * Applies the network policy for a {@link android.net.Network} with the given parameters.
      *
      * <p>Prior to a new NetworkAgent being registered, or upon notification that Carrier VCN policy
-     * may have changed via {@link VcnNetworkPolicyListener#onPolicyChanged()}, a Network Provider
-     * MUST poll for the updated Network policy based on that Network's capabilities and properties.
+     * may have changed via {@link VcnNetworkPolicyChangeListener#onPolicyChanged()}, a Network
+     * Provider MUST poll for the updated Network policy based on that Network's capabilities and
+     * properties.
      *
      * @param networkCapabilities the NetworkCapabilities to be used in determining the Network
      *     policy result for this Network.
@@ -433,7 +439,7 @@
          * @param statusCode the code for the status change encountered by this {@link
          *     VcnStatusCallback}'s subscription group.
          */
-        public abstract void onVcnStatusChanged(@VcnStatusCode int statusCode);
+        public abstract void onStatusChanged(@VcnStatusCode int statusCode);
 
         /**
          * Invoked when a VCN Gateway Connection corresponding to this callback's subscription group
@@ -442,7 +448,7 @@
          * @param networkCapabilities an array of NetworkCapabilities.NET_CAPABILITY_* capabilities
          *     for the Gateway Connection that encountered the error, for identification purposes.
          *     These will be a sorted list with no duplicates and will match {@link
-         *     VcnGatewayConnectionConfig#getRequiredUnderlyingCapabilities()} for one of the {@link
+         *     VcnGatewayConnectionConfig#getExposedCapabilities()} for one of the {@link
          *     VcnGatewayConnectionConfig}s set in the {@link VcnConfig} for this subscription
          *     group.
          * @param errorCode the code to indicate the error that occurred
@@ -470,7 +476,7 @@
      * and there is a VCN active for its specified subscription group (this may happen after the
      * callback is registered).
      *
-     * <p>{@link VcnStatusCallback#onVcnStatusChanged(int)} will be invoked on registration with the
+     * <p>{@link VcnStatusCallback#onStatusChanged(int)} will be invoked on registration with the
      * current status for the specified subscription group's VCN. If the registrant is not
      * privileged for this subscription group, {@link #VCN_STATUS_CODE_NOT_CONFIGURED} will be
      * returned.
@@ -532,17 +538,18 @@
     }
 
     /**
-     * Binder wrapper for added VcnNetworkPolicyListeners to receive signals from System Server.
+     * Binder wrapper for added VcnNetworkPolicyChangeListeners to receive signals from System
+     * Server.
      *
      * @hide
      */
     private static class VcnUnderlyingNetworkPolicyListenerBinder
             extends IVcnUnderlyingNetworkPolicyListener.Stub {
         @NonNull private final Executor mExecutor;
-        @NonNull private final VcnNetworkPolicyListener mListener;
+        @NonNull private final VcnNetworkPolicyChangeListener mListener;
 
         private VcnUnderlyingNetworkPolicyListenerBinder(
-                Executor executor, VcnNetworkPolicyListener listener) {
+                Executor executor, VcnNetworkPolicyChangeListener listener) {
             mExecutor = executor;
             mListener = listener;
         }
@@ -573,7 +580,7 @@
         @Override
         public void onVcnStatusChanged(@VcnStatusCode int statusCode) {
             Binder.withCleanCallingIdentity(
-                    () -> mExecutor.execute(() -> mCallback.onVcnStatusChanged(statusCode)));
+                    () -> mExecutor.execute(() -> mCallback.onStatusChanged(statusCode)));
         }
 
         // TODO(b/180521637): use ServiceSpecificException for safer Exception 'parceling'
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 6425c25..f5130bc 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -543,6 +543,16 @@
     public final native void markVintfStability();
 
     /**
+     * Use a VINTF-stability binder w/o VINTF requirements. Should be called
+     * on a binder before it is sent out of process.
+     *
+     * This must be called before the object is sent to another process.
+     *
+     * @hide
+     */
+    public final native void forceDowngradeToSystemStability();
+
+    /**
      * Flush any Binder commands pending in the current thread to the kernel
      * driver.  This can be
      * useful to call before performing an operation that may block for a long
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index d7d3e58..189a8ac 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -138,7 +138,7 @@
      */
     @UnsupportedAppUsage
     @TestApi
-    public static final boolean IS_EMULATOR = getString("ro.kernel.qemu").equals("1");
+    public static final boolean IS_EMULATOR = getString("ro.boot.qemu").equals("1");
 
     /**
      * A hardware serial number, if available. Alphanumeric only, case-insensitive.
@@ -1116,6 +1116,18 @@
     }
 
     /**
+     * A multiplier for various timeouts on the system.
+     *
+     * The intent is that products targeting software emulators that are orders of magnitude slower
+     * than real hardware may set this to a large number. On real devices and hardware-accelerated
+     * virtualized devices this should not be set.
+     *
+     * @hide
+     */
+    public static final int HW_TIMEOUT_MULTIPLIER =
+        SystemProperties.getInt("ro.hw_timeout_multiplier", 1);
+
+    /**
      * True if Treble is enabled and required for this device.
      *
      * @hide
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index a4d6c38..0264d23 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -1235,9 +1235,9 @@
     }
 
     /**
-     * Creates a directory with name {@code name} under an existing directory {@code baseDir}.
-     * Returns a {@code File} object representing the directory on success, {@code null} on
-     * failure.
+     * Creates a directory with name {@code name} under an existing directory {@code baseDir} if it
+     * doesn't exist already. Returns a {@code File} object representing the directory if it exists
+     * and {@code null} if not.
      *
      * @hide
      */
@@ -1247,13 +1247,23 @@
         return createDir(dir) ? dir : null;
     }
 
-    /** @hide */
+    /**
+     * Ensure the given directory exists, creating it if needed. This method is threadsafe.
+     *
+     * @return false if the directory doesn't exist and couldn't be created
+     *
+     * @hide
+     */
     public static boolean createDir(File dir) {
+        if (dir.mkdir()) {
+            return true;
+        }
+
         if (dir.exists()) {
             return dir.isDirectory();
         }
 
-        return dir.mkdir();
+        return false;
     }
 
     /**
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 8c2ca6d..9d16f18 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -16,8 +16,11 @@
 
 package android.os;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.system.ErrnoException;
@@ -104,6 +107,7 @@
      * @hide
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @SystemApi(client = MODULE_LIBRARIES)
     public static final int VPN_UID = 1016;
 
     /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6d3b58c..385a815 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1009,8 +1009,8 @@
      * Output: Nothing.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_MANAGE_ALL_SUBSCRIPTIONS_SETTINGS =
-            "android.settings.MANAGE_ALL_SUBSCRIPTIONS_SETTINGS";
+    public static final String ACTION_MANAGE_ALL_SIM_PROFILES_SETTINGS =
+            "android.settings.MANAGE_ALL_SIM_PROFILES_SETTINGS";
 
     /**
      * Activity Action: Show screen for controlling which apps can draw on top of other apps.
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 9cb76c1..a3a910a 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -4557,6 +4557,15 @@
         public static final String VOICE_REG_STATE = "voice_reg_state";
 
         /**
+         * An integer value indicating the current data service state.
+         * <p>
+         * Valid values: {@link ServiceState#STATE_IN_SERVICE},
+         * {@link ServiceState#STATE_OUT_OF_SERVICE}, {@link ServiceState#STATE_EMERGENCY_ONLY},
+         * {@link ServiceState#STATE_POWER_OFF}.
+         */
+        public static final String DATA_REG_STATE = "data_reg_state";
+
+        /**
          * The current registered operator numeric id.
          * <p>
          * In GSM/UMTS, numeric format is 3 digit country code plus 2 or 3 digit
@@ -4572,6 +4581,24 @@
          * This is the same as {@link ServiceState#getIsManualSelection()}.
          */
         public static final String IS_MANUAL_NETWORK_SELECTION = "is_manual_network_selection";
+
+        /**
+         * The current data network type.
+         * <p>
+         * This is the same as {@link TelephonyManager#getDataNetworkType()}.
+         */
+        public static final String DATA_NETWORK_TYPE = "data_network_type";
+
+        /**
+         * An integer value indicating the current duplex mode if the radio technology is LTE,
+         * LTE-CA or NR.
+         * <p>
+         * Valid values: {@link ServiceState#DUPLEX_MODE_UNKNOWN},
+         * {@link ServiceState#DUPLEX_MODE_FDD}, {@link ServiceState#DUPLEX_MODE_TDD}.
+         * <p>
+         * This is the same as {@link ServiceState#getDuplexMode()}.
+         */
+        public static final String DUPLEX_MODE = "duplex_mode";
     }
 
     /**
@@ -5284,7 +5311,8 @@
          * which network types are allowed for
          * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER},
          * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER},
-         * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER}.
+         * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER},
+         * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}.
          * <P>Type: TEXT </P>
          *
          * @hide
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index 0ae5ed7..bbb1d0ca 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -21,7 +21,6 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.compat.annotation.ChangeId;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Binder;
 import android.os.Build;
@@ -1265,6 +1264,8 @@
         // default implementation empty
     }
 
+
+
     /**
      * The callback methods need to be called on the handler thread where
      * this object was created.  If the binder did that for us it'd be nice.
@@ -1576,7 +1577,12 @@
             // default implementation empty
         }
 
-        public void onAllowedNetworkTypesChanged(Map allowedNetworkTypesList) {
+        public void onAllowedNetworkTypesChanged(int reason, long allowedNetworkType) {
+            // default implementation empty
+        }
+
+        public void onLinkCapacityEstimateChanged(
+                List<LinkCapacityEstimate> linkCapacityEstimateList) {
             // default implementation empty
         }
     }
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index a2584cae..e49c82c 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -24,7 +24,6 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.compat.annotation.ChangeId;
-import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Binder;
 import android.os.Build;
 import android.telephony.emergency.EmergencyNumber;
@@ -545,9 +544,6 @@
     /**
      * Event for changes to allowed network list based on all active subscriptions.
      *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
-     * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
      * @hide
      * @see AllowedNetworkTypesListener#onAllowedNetworkTypesChanged
      */
@@ -556,6 +552,33 @@
     public static final int EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED = 35;
 
     /**
+     * Event for changes to the legacy call state changed listener implemented by
+     * {@link PhoneStateListener#onCallStateChanged(int, String)}.  This listener variant is similar
+     * to the new {@link CallStateListener#onCallStateChanged(int)} with the important distinction
+     * that it CAN provide the phone number associated with a call.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_CALL_LOG)
+    public static final int EVENT_LEGACY_CALL_STATE_CHANGED = 36;
+
+
+    /**
+     * Event for changes to the link capacity estimate (LCE)
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     *
+     * @see LinkCapacityEstimateChangedListener#onLinkCapacityEstimateChanged
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int EVENT_LINK_CAPACITY_ESTIMATE_CHANGED = 37;
+
+
+    /**
      * @hide
      */
     @IntDef(prefix = {"EVENT_"}, value = {
@@ -593,7 +616,9 @@
             EVENT_BARRING_INFO_CHANGED,
             EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED,
             EVENT_DATA_ENABLED_CHANGED,
-            EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED
+            EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED,
+            EVENT_LEGACY_CALL_STATE_CHANGED,
+            EVENT_LINK_CAPACITY_ESTIMATE_CHANGED
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface TelephonyEvent {
@@ -1259,30 +1284,34 @@
     public interface AllowedNetworkTypesListener {
         /**
          * Callback invoked when the current allowed network type list has changed on the
-         * registered subscription.
+         * registered subscription for a specified reason.
          * Note, the registered subscription is associated with {@link TelephonyManager} object
-         * on which
-         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}
+         * on which {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}
          * was called.
          * If this TelephonyManager object was created with
          * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
          * given subscription ID. Otherwise, this callback applies to
          * {@link SubscriptionManager#getDefaultSubscriptionId()}.
          *
-         * @param allowedNetworkTypesList Map associating all allowed network type reasons
-         * ({@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER},
-         * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER},
-         * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER}, and
-         * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}) with reason's allowed
-         * network type values.
+         * @param reason an allowed network type reasons.
+         * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER
+         * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER
+         * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER
+         * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G
+         *
+         * @param allowedNetworkType an allowed network type bitmask value. (for example,
+         * the long bitmask value is {{@link TelephonyManager#NETWORK_TYPE_BITMASK_NR}|
+         * {@link TelephonyManager#NETWORK_TYPE_BITMASK_LTE}})
+         *
          * For example:
-         * map{{TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER, long type value},
-         *     {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER, long type value},
-         *     {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER, long type value},
-         *     {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, long type value}}
+         * If the latest allowed network type is changed by user, then the system
+         * notifies the {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER} and
+         * long type value}.
          */
         @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-        void onAllowedNetworkTypesChanged(@NonNull Map<Integer, Long> allowedNetworkTypesList);
+        void onAllowedNetworkTypesChanged(
+                @TelephonyManager.AllowedNetworkTypesReason int reason,
+                @TelephonyManager.NetworkTypeBitMask long allowedNetworkType);
     }
 
     /**
@@ -1330,10 +1359,7 @@
 
     /**
      * Interface for current physical channel configuration listener.
-     *
-     * @hide
      */
-    @SystemApi
     public interface PhysicalChannelConfigListener {
         /**
          * Callback invoked when the current physical channel configuration has changed
@@ -1363,6 +1389,25 @@
             @TelephonyManager.DataEnabledReason int reason);
     }
 
+    /**
+     * Interface for link capacity estimate changed listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface LinkCapacityEstimateChangedListener {
+        /**
+         * Callback invoked when the link capacity estimate (LCE) changes
+         *
+         * @param linkCapacityEstimateList a list of {@link LinkCapacityEstimate}
+         * The list size is at least 1.
+         * In case of a dual connected network, the list size could be 2.
+         * Use {@link LinkCapacityEstimate#getType()} to get the type of each element.
+         */
+        @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+        void onLinkCapacityEstimateChanged(
+                @NonNull List<LinkCapacityEstimate> linkCapacityEstimateList);
+    }
 
     /**
      * The callback methods need to be called on the handler thread where
@@ -1697,14 +1742,26 @@
                             enabled, reason)));
         }
 
-        public void onAllowedNetworkTypesChanged(Map allowedNetworkTypesList) {
+        public void onAllowedNetworkTypesChanged(int reason, long allowedNetworkType) {
             AllowedNetworkTypesListener listener =
                     (AllowedNetworkTypesListener) mTelephonyCallbackWeakRef.get();
             if (listener == null) return;
 
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(
-                            () -> listener.onAllowedNetworkTypesChanged(allowedNetworkTypesList)));
+                            () -> listener.onAllowedNetworkTypesChanged(reason,
+                                    allowedNetworkType)));
+        }
+
+        public void onLinkCapacityEstimateChanged(
+                List<LinkCapacityEstimate> linkCapacityEstimateList) {
+            LinkCapacityEstimateChangedListener listener =
+                    (LinkCapacityEstimateChangedListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onLinkCapacityEstimateChanged(
+                            linkCapacityEstimateList)));
         }
     }
 }
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 15d1a59..b111ec3 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -825,24 +825,40 @@
     }
 
     /**
-     * Notify emergency number list changed on certain subscription.
-     *
-     * @param slotIndex for which emergency number list changed. Can be derived from subId except
-     * when subId is invalid.
-     * @param subId for which emergency number list changed.
+     * Notify the allowed network types has changed for a specific subscription and the specific
+     * reason.
+     * @param slotIndex for which allowed network types changed.
+     * @param subId for which allowed network types changed.
+     * @param reason an allowed network type reasons.
+     * @param allowedNetworkType an allowed network type bitmask value.
      */
     public void notifyAllowedNetworkTypesChanged(int slotIndex, int subId,
-            Map<Integer, Long> allowedNetworkTypeList) {
+            int reason, long allowedNetworkType) {
         try {
-            sRegistry.notifyAllowedNetworkTypesChanged(slotIndex, subId, allowedNetworkTypeList);
+            sRegistry.notifyAllowedNetworkTypesChanged(slotIndex, subId, reason,
+                    allowedNetworkType);
         } catch (RemoteException ex) {
             // system process is dead
         }
     }
 
+    /**
+     * Notify that the link capacity estimate has changed.
+     * @param slotIndex for the phone object that gets the updated link capacity estimate
+     * @param subId for subscription that gets the updated link capacity estimate
+     * @param linkCapacityEstimateList a list of {@link  LinkCapacityEstimate}
+     */
+    public void notifyLinkCapacityEstimateChanged(int slotIndex, int subId,
+            List<LinkCapacityEstimate> linkCapacityEstimateList) {
+        try {
+            sRegistry.notifyLinkCapacityEstimateChanged(slotIndex, subId, linkCapacityEstimateList);
+        } catch (RemoteException ex) {
+            // system server crash
+        }
+    }
+
     public @NonNull Set<Integer> getEventsFromCallback(
             @NonNull TelephonyCallback telephonyCallback) {
-
         Set<Integer> eventList = new ArraySet<>();
 
         if (telephonyCallback instanceof TelephonyCallback.ServiceStateListener) {
@@ -973,6 +989,10 @@
             eventList.add(TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED);
         }
 
+        if (telephonyCallback instanceof TelephonyCallback.LinkCapacityEstimateChangedListener) {
+            eventList.add(TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED);
+        }
+
         return eventList;
     }
 
diff --git a/core/java/android/uwb/AngleMeasurement.java b/core/java/android/uwb/AngleMeasurement.java
index 9df213b..3d60373 100644
--- a/core/java/android/uwb/AngleMeasurement.java
+++ b/core/java/android/uwb/AngleMeasurement.java
@@ -38,9 +38,33 @@
     private final double mErrorRadians;
     private final double mConfidenceLevel;
 
-    private AngleMeasurement(double radians, double errorRadians, double confidenceLevel) {
+    /**
+     * Constructs a new {@link AngleMeasurement} object
+     *
+     * @param radians the angle in radians
+     * @param errorRadians the error of the angle measurement in radians
+     * @param confidenceLevel confidence level of the angle measurement
+     *
+     * @throws IllegalArgumentException if the radians, errorRadians, or confidenceLevel is out of
+     *                                  allowed range
+     */
+    public AngleMeasurement(
+            @FloatRange(from = -Math.PI, to = +Math.PI) double radians,
+            @FloatRange(from = 0.0, to = +Math.PI) double errorRadians,
+            @FloatRange(from = 0.0, to = 1.0) double confidenceLevel) {
+        if (radians < -Math.PI || radians > Math.PI) {
+            throw new IllegalArgumentException("Invalid radians: " + radians);
+        }
         mRadians = radians;
+
+        if (errorRadians < 0.0 || errorRadians > Math.PI) {
+            throw new IllegalArgumentException("Invalid error radians: " + errorRadians);
+        }
         mErrorRadians = errorRadians;
+
+        if (confidenceLevel < 0.0 || confidenceLevel > 1.0) {
+            throw new IllegalArgumentException("Invalid confidence level: " + confidenceLevel);
+        }
         mConfidenceLevel = confidenceLevel;
     }
 
@@ -122,11 +146,7 @@
             new Creator<AngleMeasurement>() {
                 @Override
                 public AngleMeasurement createFromParcel(Parcel in) {
-                    Builder builder = new Builder();
-                    builder.setRadians(in.readDouble());
-                    builder.setErrorRadians(in.readDouble());
-                    builder.setConfidenceLevel(in.readDouble());
-                    return builder.build();
+                    return new AngleMeasurement(in.readDouble(), in.readDouble(), in.readDouble());
                 }
 
                 @Override
@@ -134,82 +154,4 @@
                     return new AngleMeasurement[size];
                 }
     };
-
-    /**
-     * Builder class for {@link AngleMeasurement}.
-     */
-    public static final class Builder {
-        private double mRadians = Double.NaN;
-        private double mErrorRadians = Double.NaN;
-        private double mConfidenceLevel = Double.NaN;
-
-        /**
-         * Set the angle in radians
-         *
-         * @param radians angle in radians
-         * @throws IllegalArgumentException if angle exceeds allowed limits of [-Math.PI, +Math.PI]
-         */
-        @NonNull
-        public Builder setRadians(double radians) {
-            if (radians < -Math.PI || radians > Math.PI) {
-                throw new IllegalArgumentException("Invalid radians: " + radians);
-            }
-            mRadians = radians;
-            return this;
-        }
-
-        /**
-         * Set the angle error in radians
-         *
-         * @param errorRadians error of the angle in radians
-         * @throws IllegalArgumentException if the error exceeds the allowed limits of [0, +Math.PI]
-         */
-        @NonNull
-        public Builder setErrorRadians(double errorRadians) {
-            if (errorRadians < 0.0 || errorRadians > Math.PI) {
-                throw new IllegalArgumentException(
-                        "Invalid error radians: " + errorRadians);
-            }
-            mErrorRadians = errorRadians;
-            return this;
-        }
-
-        /**
-         * Set the angle confidence level
-         *
-         * @param confidenceLevel level of confidence of the angle measurement
-         * @throws IllegalArgumentException if the error exceeds the allowed limits of [0.0, 1.0]
-         */
-        @NonNull
-        public Builder setConfidenceLevel(double confidenceLevel) {
-            if (confidenceLevel < 0.0 || confidenceLevel > 1.0) {
-                throw new IllegalArgumentException(
-                        "Invalid confidence level: " + confidenceLevel);
-            }
-            mConfidenceLevel = confidenceLevel;
-            return this;
-        }
-
-        /**
-         * Build the {@link AngleMeasurement} object
-         *
-         * @throws IllegalStateException if angle, error, or confidence values are missing
-         */
-        @NonNull
-        public AngleMeasurement build() {
-            if (Double.isNaN(mRadians)) {
-                throw new IllegalStateException("Angle is not set");
-            }
-
-            if (Double.isNaN(mErrorRadians)) {
-                throw new IllegalStateException("Angle error is not set");
-            }
-
-            if (Double.isNaN(mConfidenceLevel)) {
-                throw new IllegalStateException("Angle confidence level is not set");
-            }
-
-            return new AngleMeasurement(mRadians, mErrorRadians, mConfidenceLevel);
-        }
-    }
 }
diff --git a/core/java/android/uwb/AngleOfArrivalMeasurement.java b/core/java/android/uwb/AngleOfArrivalMeasurement.java
index 3d8626b..db04ad1 100644
--- a/core/java/android/uwb/AngleOfArrivalMeasurement.java
+++ b/core/java/android/uwb/AngleOfArrivalMeasurement.java
@@ -116,9 +116,8 @@
             new Creator<AngleOfArrivalMeasurement>() {
                 @Override
                 public AngleOfArrivalMeasurement createFromParcel(Parcel in) {
-                    Builder builder = new Builder();
-
-                    builder.setAzimuth(in.readParcelable(AngleMeasurement.class.getClassLoader()));
+                    Builder builder =
+                            new Builder(in.readParcelable(AngleMeasurement.class.getClassLoader()));
 
                     builder.setAltitude(in.readParcelable(AngleMeasurement.class.getClassLoader()));
 
@@ -135,18 +134,16 @@
      * Builder class for {@link AngleOfArrivalMeasurement}.
      */
     public static final class Builder {
-        private AngleMeasurement mAzimuthAngleMeasurement = null;
+        private final AngleMeasurement mAzimuthAngleMeasurement;
         private AngleMeasurement mAltitudeAngleMeasurement = null;
 
         /**
-         * Set the azimuth angle
+         * Constructs an {@link AngleOfArrivalMeasurement} object
          *
-         * @param azimuthAngle azimuth angle
+         * @param azimuthAngle the azimuth angle of the measurement
          */
-        @NonNull
-        public Builder setAzimuth(@NonNull AngleMeasurement azimuthAngle) {
+        public Builder(@NonNull AngleMeasurement azimuthAngle) {
             mAzimuthAngleMeasurement = azimuthAngle;
-            return this;
         }
 
         /**
@@ -162,15 +159,9 @@
 
         /**
          * Build the {@link AngleOfArrivalMeasurement} object
-         *
-         * @throws IllegalStateException if the required azimuth angle is not provided
          */
         @NonNull
         public AngleOfArrivalMeasurement build() {
-            if (mAzimuthAngleMeasurement == null) {
-                throw new IllegalStateException("Azimuth angle measurement is not set");
-            }
-
             return new AngleOfArrivalMeasurement(mAzimuthAngleMeasurement,
                     mAltitudeAngleMeasurement);
         }
diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl
index 468a69c..4036892 100644
--- a/core/java/android/uwb/IUwbAdapter.aidl
+++ b/core/java/android/uwb/IUwbAdapter.aidl
@@ -62,9 +62,6 @@
   /**
    * Request to open a new ranging session
    *
-   * This function must return before calling any functions in
-   * IUwbAdapterCallbacks.
-   *
    * This function does not start the ranging session, but all necessary
    * components must be initialized and ready to start a new ranging
    * session prior to calling IUwbAdapterCallback#onRangingOpened.
@@ -77,12 +74,16 @@
    * RANGING_SESSION_OPEN_THRESHOLD_MS milliseconds of #openRanging being called
    * if the ranging session fails to be opened.
    *
+   * If the provided sessionHandle is already open for the calling client, then
+   * #onRangingOpenFailed must be called and the new session must not be opened.
+   *
+   * @param sessionHandle the session handle to open ranging for
    * @param rangingCallbacks the callbacks used to deliver ranging information
    * @param parameters the configuration to use for ranging
-   * @return a SessionHandle used to identify this ranging request
    */
-  SessionHandle openRanging(in IUwbRangingCallbacks rangingCallbacks,
-                            in PersistableBundle parameters);
+  void openRanging(in SessionHandle sessionHandle,
+                   in IUwbRangingCallbacks rangingCallbacks,
+                   in PersistableBundle parameters);
 
   /**
    * Request to start ranging
diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java
index c0d8187..85f2c1c 100644
--- a/core/java/android/uwb/RangingManager.java
+++ b/core/java/android/uwb/RangingManager.java
@@ -17,6 +17,7 @@
 package android.uwb;
 
 import android.annotation.NonNull;
+import android.os.CancellationSignal;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.util.Log;
@@ -32,6 +33,7 @@
 
     private final IUwbAdapter mAdapter;
     private final Hashtable<SessionHandle, RangingSession> mRangingSessionTable = new Hashtable<>();
+    private int mNextSessionId = 1;
 
     public RangingManager(IUwbAdapter adapter) {
         mAdapter = adapter;
@@ -44,29 +46,26 @@
      * @param executor {@link Executor} to run callbacks
      * @param callbacks {@link RangingSession.Callback} to associate with the {@link RangingSession}
      *                  that is being opened.
-     * @return a new {@link RangingSession}
+     * @return a {@link CancellationSignal} that may be used to cancel the opening of the
+     *         {@link RangingSession}.
      */
-    public RangingSession openSession(@NonNull PersistableBundle params, @NonNull Executor executor,
+    public CancellationSignal openSession(@NonNull PersistableBundle params,
+            @NonNull Executor executor,
             @NonNull RangingSession.Callback callbacks) {
-        SessionHandle sessionHandle;
-        try {
-            sessionHandle = mAdapter.openRanging(this, params);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-
         synchronized (this) {
-            if (hasSession(sessionHandle)) {
-                Log.w(TAG, "Newly created session unexpectedly reuses an active SessionHandle");
-                executor.execute(() -> callbacks.onClosed(
-                        RangingSession.Callback.REASON_GENERIC_ERROR,
-                        new PersistableBundle()));
-            }
-
+            SessionHandle sessionHandle = new SessionHandle(mNextSessionId++);
             RangingSession session =
                     new RangingSession(executor, callbacks, mAdapter, sessionHandle);
             mRangingSessionTable.put(sessionHandle, session);
-            return session;
+            try {
+                mAdapter.openRanging(sessionHandle, this, params);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+
+            CancellationSignal cancellationSignal = new CancellationSignal();
+            cancellationSignal.setOnCancelListener(() -> session.close());
+            return cancellationSignal;
         }
     }
 
diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java
index 63a6d05..844bbbe 100644
--- a/core/java/android/uwb/UwbManager.java
+++ b/core/java/android/uwb/UwbManager.java
@@ -25,6 +25,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
+import android.os.CancellationSignal;
 import android.os.IBinder;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
@@ -228,14 +229,14 @@
      * @param callbacks {@link RangingSession.Callback} to associate with the
      *                  {@link RangingSession} that is being opened.
      *
-     * @return an {@link AutoCloseable} that is able to be used to close or cancel the opening of a
+     * @return an {@link CancellationSignal} that is able to be used to cancel the opening of a
      *         {@link RangingSession} that has been requested through {@link #openRangingSession}
      *         but has not yet been made available by
      *         {@link RangingSession.Callback#onOpened(RangingSession)}.
      */
     @NonNull
     @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
-    public AutoCloseable openRangingSession(@NonNull PersistableBundle parameters,
+    public CancellationSignal openRangingSession(@NonNull PersistableBundle parameters,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull RangingSession.Callback callbacks) {
         return mRangingManager.openSession(parameters, executor, callbacks);
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index a86984f..4a1d685 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -9,6 +9,7 @@
 ogunwale@google.com
 jjaggi@google.com
 roosa@google.com
+jreck@google.com
 
 # Display
 per-file Display*.java = file:/services/core/java/com/android/server/display/OWNERS
@@ -80,5 +81,6 @@
 per-file Window*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
 
 # Scroll Capture
+per-file *ScrollCapture*.aidl = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
 per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
 per-file *CaptureHelper*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 10f6c61..96818fa 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -67,7 +67,7 @@
  * <p>Content capture provides real-time, continuous capture of application activity, display and
  * events to an intelligence service that is provided by the Android system. The intelligence
  * service then uses that info to mediate and speed user journey through different apps. For
- * example, when the user receives a restaurant address in a chat app and switchs to a map app
+ * example, when the user receives a restaurant address in a chat app and switches to a map app
  * to search for that restaurant, the intelligence service could offer an autofill dialog to
  * let the user automatically select its address.
  *
diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS
index 718076b..64570a8 100644
--- a/core/java/android/widget/OWNERS
+++ b/core/java/android/widget/OWNERS
@@ -5,6 +5,8 @@
 adamp@google.com
 aurimas@google.com
 siyamed@google.com
+mount@google.com
+njawad@google.com
 
 per-file TextView.java, EditText.java, Editor.java = siyamed@google.com, nona@google.com, clarabayarri@google.com
 
diff --git a/core/java/com/android/internal/app/OWNERS b/core/java/com/android/internal/app/OWNERS
index 7ade05c..e6c911e 100644
--- a/core/java/com/android/internal/app/OWNERS
+++ b/core/java/com/android/internal/app/OWNERS
@@ -3,6 +3,9 @@
 per-file *Chooser* = file:/packages/SystemUI/OWNERS
 per-file SimpleIconFactory.java = file:/packages/SystemUI/OWNERS
 per-file NetInitiatedActivity.java = file:/location/java/android/location/OWNERS
-per-file IVoice* = file:/core/java/android/service/voice/OWNERS
-per-file *Hotword* = file:/core/java/android/service/voice/OWNERS
 per-file *BatteryStats* = file:/BATTERY_STATS_OWNERS
+
+# Voice Interaction
+per-file *Assist* = file:/core/java/android/service/voice/OWNERS
+per-file *Hotword* = file:/core/java/android/service/voice/OWNERS
+per-file *Voice* = file:/core/java/android/service/voice/OWNERS
diff --git a/core/java/com/android/internal/compat/AndroidBuildClassifier.java b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
index 0b937fa..364db06 100644
--- a/core/java/com/android/internal/compat/AndroidBuildClassifier.java
+++ b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
@@ -31,4 +31,14 @@
     public boolean isFinalBuild() {
         return "REL".equals(Build.VERSION.CODENAME);
     }
+
+    /**
+     * The current platform SDK version.
+     */
+    public int platformTargetSdk() {
+        if (isFinalBuild()) {
+            return Build.VERSION.SDK_INT;
+        }
+        return Build.VERSION_CODES.CUR_DEVELOPMENT;
+    }
 }
diff --git a/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java b/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java
index 1c222a7..9a02b7b 100644
--- a/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java
@@ -40,8 +40,7 @@
         overrides = new HashMap<>();
         for (int i = 0; i < keyCount; i++) {
             long key = in.readLong();
-            PackageOverride override = in.readParcelable(PackageOverride.class.getClassLoader());
-            overrides.put(key, override);
+            overrides.put(key, PackageOverride.createFromParcel(in));
         }
     }
 
@@ -55,7 +54,7 @@
         dest.writeInt(overrides.size());
         for (Long key : overrides.keySet()) {
             dest.writeLong(key);
-            dest.writeParcelable(overrides.get(key), 0);
+            overrides.get(key).writeToParcel(dest);
         }
     }
 
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index 60213e4..78d1d22 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -151,15 +151,23 @@
     void setOverrides(in CompatibilityChangeConfig overrides, in String packageName);
 
     /**
-     * Adds overrides to compatibility changes.
+     * Adds overrides to compatibility changes on release builds.
      *
-     * <p>Kills the app to allow the changes to take effect.
+     * <p>The caller to this API needs to hold
+     * {@code android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD} and all change ids
+     * in {@code overrides} need to annotated with {@link android.compat.annotation.Overridable}.
+     *
+     * A release build in this definition means that {@link android.os.Build#IS_DEBUGGABLE} needs to
+     * be {@code false}.
+     *
+     * <p>Note that this does not kill the app, and therefore overrides read from the app process
+     * will not be updated. Overrides read from the system process do take effect.
      *
      * @param overrides   parcelable containing the compat change overrides to be applied
      * @param packageName the package name of the app whose changes will be overridden
      * @throws SecurityException if overriding changes is not permitted
      */
-    void setOverridesFromInstaller(in CompatibilityOverrideConfig overrides, in String packageName);
+    void setOverridesOnReleaseBuilds(in CompatibilityOverrideConfig overrides, in String packageName);
 
     /**
      * Adds overrides to compatibility changes.
diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.java b/core/java/com/android/internal/compat/OverrideAllowedState.java
index c0bbe50..e408be2 100644
--- a/core/java/com/android/internal/compat/OverrideAllowedState.java
+++ b/core/java/com/android/internal/compat/OverrideAllowedState.java
@@ -34,7 +34,8 @@
             DISABLED_NON_TARGET_SDK,
             DISABLED_TARGET_SDK_TOO_HIGH,
             DEFERRED_VERIFICATION,
-            LOGGING_ONLY_CHANGE
+            LOGGING_ONLY_CHANGE,
+            PLATFORM_TOO_OLD
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface State {
@@ -65,6 +66,10 @@
      * Change is marked as logging only, and cannot be toggled.
      */
     public static final int LOGGING_ONLY_CHANGE = 5;
+    /**
+     * Change is gated by a target sdk version newer than the current platform sdk version.
+     */
+    public static final int PLATFORM_TOO_OLD = 6;
 
     @State
     public final int state;
@@ -123,6 +128,11 @@
                 throw new SecurityException(String.format(
                         "Cannot override %1$d because it is marked as a logging-only change.",
                         changeId));
+            case PLATFORM_TOO_OLD:
+                throw new SecurityException(String.format(
+                        "Cannot override %1$d for %2$s because the change's targetSdk threshold "
+                                + "(%3$d) is above the platform sdk.",
+                        changeId, packageName, changeIdTargetSdk));
         }
     }
 
@@ -170,6 +180,8 @@
                 return "DEFERRED_VERIFICATION";
             case LOGGING_ONLY_CHANGE:
                 return "LOGGING_ONLY_CHANGE";
+            case PLATFORM_TOO_OLD:
+                return "PLATFORM_TOO_OLD";
         }
         return "UNKNOWN";
     }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index c220428..5fea76a 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -41,7 +41,6 @@
 import android.os.ZygoteProcess;
 import android.os.storage.StorageManager;
 import android.provider.DeviceConfig;
-import android.security.keystore.AndroidKeyStoreProvider;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
@@ -74,7 +73,6 @@
 import java.io.InputStreamReader;
 import java.security.Provider;
 import java.security.Security;
-import java.util.Optional;
 
 /**
  * Startup class for the zygote process.
@@ -227,17 +225,7 @@
         // AndroidKeyStoreProvider.install() manipulates the list of JCA providers to insert
         // preferred providers. Note this is not done via security.properties as the JCA providers
         // are not on the classpath in the case of, for example, raw dalvikvm runtimes.
-        // TODO b/171305684 This code is used to conditionally enable the installation of the
-        //      Keystore 2.0 provider to enable teams adjusting to Keystore 2.0 at their own
-        //      pace. This code will be removed when all calling code was adjusted to
-        //      Keystore 2.0.
-        Optional<Boolean> keystore2_enabled =
-                android.sysprop.Keystore2Properties.keystore2_enabled();
-        if (keystore2_enabled.isPresent() && keystore2_enabled.get()) {
-            android.security.keystore2.AndroidKeyStoreProvider.install();
-        } else {
-            AndroidKeyStoreProvider.install();
-        }
+        android.security.keystore2.AndroidKeyStoreProvider.install();
         Log.i(TAG, "Installed AndroidKeyStoreProvider in "
                 + (SystemClock.uptimeMillis() - startTime) + "ms.");
         Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index ee94ef8..b90722c 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -21,6 +21,7 @@
 import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
 import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.LinkCapacityEstimate;
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.PhoneCapability;
 import android.telephony.PhysicalChannelConfig;
@@ -71,5 +72,6 @@
     void onBarringInfoChanged(in BarringInfo barringInfo);
     void onPhysicalChannelConfigChanged(in List<PhysicalChannelConfig> configs);
     void onDataEnabledChanged(boolean enabled, int reason);
-    void onAllowedNetworkTypesChanged(in Map allowedNetworkTypeList);
+    void onAllowedNetworkTypesChanged(in int reason, in long allowedNetworkType);
+    void onLinkCapacityEstimateChanged(in List<LinkCapacityEstimate> linkCapacityEstimateList);
 }
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 8d69158..0e35c84 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -23,6 +23,7 @@
 import android.telephony.CallQuality;
 import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
+import android.telephony.LinkCapacityEstimate;
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.PhoneCapability;
@@ -94,5 +95,7 @@
     void notifyPhysicalChannelConfigForSubscriber(in int subId,
             in List<PhysicalChannelConfig> configs);
     void notifyDataEnabled(in int phoneId, int subId, boolean enabled, int reason);
-    void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in Map allowedNetworkTypeList);
+    void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in int reason, in long allowedNetworkType);
+    void notifyLinkCapacityEstimateChanged(in int phoneId, in int subId,
+            in List<LinkCapacityEstimate> linkCapacityEstimateList);
 }
diff --git a/core/java/com/android/internal/util/LocationPermissionChecker.java b/core/java/com/android/internal/util/LocationPermissionChecker.java
deleted file mode 100644
index d67bd7a..0000000
--- a/core/java/com/android/internal/util/LocationPermissionChecker.java
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.util;
-
-import android.Manifest;
-import android.annotation.IntDef;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.location.LocationManager;
-import android.net.NetworkStack;
-import android.os.Binder;
-import android.os.Build;
-import android.os.UserHandle;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-
-/**
- * The methods used for location permission and location mode checking.
- *
- * @hide
- */
-public class LocationPermissionChecker {
-
-    private static final String TAG = "LocationPermissionChecker";
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"LOCATION_PERMISSION_CHECK_STATUS_"}, value = {
-        SUCCEEDED,
-        ERROR_LOCATION_MODE_OFF,
-        ERROR_LOCATION_PERMISSION_MISSING,
-    })
-    public @interface LocationPermissionCheckStatus{}
-
-    // The location permission check succeeded.
-    public static final int SUCCEEDED = 0;
-    // The location mode turns off for the caller.
-    public static final int ERROR_LOCATION_MODE_OFF = 1;
-    // The location permission isn't granted for the caller.
-    public static final int ERROR_LOCATION_PERMISSION_MISSING = 2;
-
-    private final Context mContext;
-    private final AppOpsManager mAppOpsManager;
-
-    public LocationPermissionChecker(Context context) {
-        mContext = context;
-        mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
-    }
-
-    /**
-     * Check location permission granted by the caller.
-     *
-     * This API check if the location mode enabled for the caller and the caller has
-     * ACCESS_COARSE_LOCATION permission is targetSDK<29, otherwise, has ACCESS_FINE_LOCATION.
-     *
-     * @param pkgName package name of the application requesting access
-     * @param featureId The feature in the package
-     * @param uid The uid of the package
-     * @param message A message describing why the permission was checked. Only needed if this is
-     *                not inside of a two-way binder call from the data receiver
-     *
-     * @return {@code true} returns if the caller has location permission and the location mode is
-     *         enabled.
-     */
-    public boolean checkLocationPermission(String pkgName, @Nullable String featureId,
-            int uid, @Nullable String message) {
-        return checkLocationPermissionInternal(pkgName, featureId, uid, message) == SUCCEEDED;
-    }
-
-    /**
-     * Check location permission granted by the caller.
-     *
-     * This API check if the location mode enabled for the caller and the caller has
-     * ACCESS_COARSE_LOCATION permission is targetSDK<29, otherwise, has ACCESS_FINE_LOCATION.
-     * Compared with {@link #checkLocationPermission(String, String, int, String)}, this API returns
-     * the detail information about the checking result, including the reason why it's failed and
-     * logs the error for the caller.
-     *
-     * @param pkgName package name of the application requesting access
-     * @param featureId The feature in the package
-     * @param uid The uid of the package
-     * @param message A message describing why the permission was checked. Only needed if this is
-     *                not inside of a two-way binder call from the data receiver
-     *
-     * @return {@link LocationPermissionCheckStatus} the result of the location permission check.
-     */
-    public @LocationPermissionCheckStatus int checkLocationPermissionWithDetailInfo(
-            String pkgName, @Nullable String featureId, int uid, @Nullable String message) {
-        final int result = checkLocationPermissionInternal(pkgName, featureId, uid, message);
-        switch (result) {
-            case ERROR_LOCATION_MODE_OFF:
-                Log.e(TAG, "Location mode is disabled for the device");
-                break;
-            case ERROR_LOCATION_PERMISSION_MISSING:
-                Log.e(TAG, "UID " + uid + " has no location permission");
-                break;
-        }
-        return result;
-    }
-
-    /**
-     * Enforce the caller has location permission.
-     *
-     * This API determines if the location mode enabled for the caller and the caller has
-     * ACCESS_COARSE_LOCATION permission is targetSDK<29, otherwise, has ACCESS_FINE_LOCATION.
-     * SecurityException is thrown if the caller has no permission or the location mode is disabled.
-     *
-     * @param pkgName package name of the application requesting access
-     * @param featureId The feature in the package
-     * @param uid The uid of the package
-     * @param message A message describing why the permission was checked. Only needed if this is
-     *                not inside of a two-way binder call from the data receiver
-     */
-    public void enforceLocationPermission(String pkgName, @Nullable String featureId, int uid,
-            @Nullable String message) throws SecurityException {
-        final int result = checkLocationPermissionInternal(pkgName, featureId, uid, message);
-
-        switch (result) {
-            case ERROR_LOCATION_MODE_OFF:
-                throw new SecurityException("Location mode is disabled for the device");
-            case ERROR_LOCATION_PERMISSION_MISSING:
-                throw new SecurityException("UID " + uid + " has no location permission");
-        }
-    }
-
-    private int checkLocationPermissionInternal(String pkgName, @Nullable String featureId,
-            int uid, @Nullable String message) {
-        checkPackage(uid, pkgName);
-
-        // Apps with NETWORK_SETTINGS, NETWORK_SETUP_WIZARD, NETWORK_STACK & MAINLINE_NETWORK_STACK
-        // are granted a bypass.
-        if (checkNetworkSettingsPermission(uid) || checkNetworkSetupWizardPermission(uid)
-                || checkNetworkStackPermission(uid) || checkMainlineNetworkStackPermission(uid)) {
-            return SUCCEEDED;
-        }
-
-        // Location mode must be enabled
-        if (!isLocationModeEnabled()) {
-            return ERROR_LOCATION_MODE_OFF;
-        }
-
-        // LocationAccess by App: caller must have Coarse/Fine Location permission to have access to
-        // location information.
-        if (!checkCallersLocationPermission(pkgName, featureId, uid,
-                true /* coarseForTargetSdkLessThanQ */, message)) {
-            return ERROR_LOCATION_PERMISSION_MISSING;
-        }
-        return SUCCEEDED;
-    }
-
-    /**
-     * Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION or
-     * android.Manifest.permission.ACCESS_COARSE_LOCATION (depending on config/targetSDK level)
-     * and a corresponding app op is allowed for this package and uid.
-     *
-     * @param pkgName PackageName of the application requesting access
-     * @param featureId The feature in the package
-     * @param uid The uid of the package
-     * @param coarseForTargetSdkLessThanQ If true and the targetSDK < Q then will check for COARSE
-     *                                    else (false or targetSDK >= Q) then will check for FINE
-     * @param message A message describing why the permission was checked. Only needed if this is
-     *                not inside of a two-way binder call from the data receiver
-     */
-    public boolean checkCallersLocationPermission(String pkgName, @Nullable String featureId,
-            int uid, boolean coarseForTargetSdkLessThanQ, @Nullable String message) {
-
-        boolean isTargetSdkLessThanQ = isTargetSdkLessThan(pkgName, Build.VERSION_CODES.Q, uid);
-
-        String permissionType = Manifest.permission.ACCESS_FINE_LOCATION;
-        if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
-            // Having FINE permission implies having COARSE permission (but not the reverse)
-            permissionType = Manifest.permission.ACCESS_COARSE_LOCATION;
-        }
-        if (getUidPermission(permissionType, uid) == PackageManager.PERMISSION_DENIED) {
-            return false;
-        }
-
-        // Always checking FINE - even if will not enforce. This will record the request for FINE
-        // so that a location request by the app is surfaced to the user.
-        boolean isFineLocationAllowed = noteAppOpAllowed(
-                AppOpsManager.OPSTR_FINE_LOCATION, pkgName, featureId, uid, message);
-        if (isFineLocationAllowed) {
-            return true;
-        }
-        if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
-            return noteAppOpAllowed(AppOpsManager.OPSTR_COARSE_LOCATION, pkgName, featureId, uid,
-                    message);
-        }
-        return false;
-    }
-
-    /**
-     * Retrieves a handle to LocationManager (if not already done) and check if location is enabled.
-     */
-    public boolean isLocationModeEnabled() {
-        final LocationManager LocationManager =
-                (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
-        try {
-            return LocationManager.isLocationEnabledForUser(UserHandle.of(
-                    getCurrentUser()));
-        } catch (Exception e) {
-            Log.e(TAG, "Failure to get location mode via API, falling back to settings", e);
-            return false;
-        }
-    }
-
-    private boolean isTargetSdkLessThan(String packageName, int versionCode, int callingUid) {
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            if (mContext.getPackageManager().getApplicationInfoAsUser(
-                    packageName, 0,
-                    UserHandle.getUserHandleForUid(callingUid)).targetSdkVersion
-                    < versionCode) {
-                return true;
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            // In case of exception, assume unknown app (more strict checking)
-            // Note: This case will never happen since checkPackage is
-            // called to verify validity before checking App's version.
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-        return false;
-    }
-
-    private boolean noteAppOpAllowed(String op, String pkgName, @Nullable String featureId,
-            int uid, @Nullable String message) {
-        return mAppOpsManager.noteOp(op, uid, pkgName, featureId, message)
-                == AppOpsManager.MODE_ALLOWED;
-    }
-
-    private void checkPackage(int uid, String pkgName)
-            throws SecurityException {
-        if (pkgName == null) {
-            throw new SecurityException("Checking UID " + uid + " but Package Name is Null");
-        }
-        mAppOpsManager.checkPackage(uid, pkgName);
-    }
-
-    @VisibleForTesting
-    protected int getCurrentUser() {
-        return ActivityManager.getCurrentUser();
-    }
-
-    private int getUidPermission(String permissionType, int uid) {
-        // We don't care about pid, pass in -1
-        return mContext.checkPermission(permissionType, -1, uid);
-    }
-
-    /**
-     * Returns true if the |uid| holds NETWORK_SETTINGS permission.
-     */
-    public boolean checkNetworkSettingsPermission(int uid) {
-        return getUidPermission(android.Manifest.permission.NETWORK_SETTINGS, uid)
-                == PackageManager.PERMISSION_GRANTED;
-    }
-
-    /**
-     * Returns true if the |uid| holds NETWORK_SETUP_WIZARD permission.
-     */
-    public boolean checkNetworkSetupWizardPermission(int uid) {
-        return getUidPermission(android.Manifest.permission.NETWORK_SETUP_WIZARD, uid)
-                == PackageManager.PERMISSION_GRANTED;
-    }
-
-    /**
-     * Returns true if the |uid| holds NETWORK_STACK permission.
-     */
-    public boolean checkNetworkStackPermission(int uid) {
-        return getUidPermission(android.Manifest.permission.NETWORK_STACK, uid)
-                == PackageManager.PERMISSION_GRANTED;
-    }
-
-    /**
-     * Returns true if the |uid| holds MAINLINE_NETWORK_STACK permission.
-     */
-    public boolean checkMainlineNetworkStackPermission(int uid) {
-        return getUidPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, uid)
-                == PackageManager.PERMISSION_GRANTED;
-    }
-
-}
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index 276cad9..bbd738b 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -59,8 +59,6 @@
     public static final int BASE_TETHERING                                          = 0x00050000;
     public static final int BASE_NSD_MANAGER                                        = 0x00060000;
     public static final int BASE_NETWORK_STATE_TRACKER                              = 0x00070000;
-    public static final int BASE_CONNECTIVITY_MANAGER                               = 0x00080000;
-    public static final int BASE_NETWORK_AGENT                                      = 0x00081000;
     public static final int BASE_NETWORK_FACTORY                                    = 0x00083000;
     public static final int BASE_ETHERNET                                           = 0x00084000;
     public static final int BASE_LOWPAN                                             = 0x00085000;
diff --git a/core/java/com/android/internal/view/inline/OWNERS b/core/java/com/android/internal/view/inline/OWNERS
new file mode 100644
index 0000000..edfb211
--- /dev/null
+++ b/core/java/com/android/internal/view/inline/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/autofill/OWNERS
diff --git a/core/java/com/android/internal/widget/OWNERS b/core/java/com/android/internal/widget/OWNERS
index d284d51..8e68be0 100644
--- a/core/java/com/android/internal/widget/OWNERS
+++ b/core/java/com/android/internal/widget/OWNERS
@@ -1,4 +1,6 @@
 per-file PointerLocationView.java = michaelwr@google.com, svv@google.com
+per-file RecyclerView.java = mount@google.com
+per-file ViewPager.java = mount@google.com
 
 # LockSettings related
 per-file *LockPattern* = file:/services/core/java/com/android/server/locksettings/OWNERS
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index c01f741..ed84434 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -630,6 +630,12 @@
     char saveResolvedClassesDelayMsOptsBuf[
             sizeof("-Xps-save-resolved-classes-delay-ms:")-1 + PROPERTY_VALUE_MAX];
     char madviseRandomOptsBuf[sizeof("-XX:MadviseRandomAccess:")-1 + PROPERTY_VALUE_MAX];
+    char madviseWillNeedFileSizeVdex[
+            sizeof("-XMadviseWillNeedVdexFileSize:")-1 + PROPERTY_VALUE_MAX];
+    char madviseWillNeedFileSizeOdex[
+            sizeof("-XMadviseWillNeedOdexFileSize:")-1 + PROPERTY_VALUE_MAX];
+    char madviseWillNeedFileSizeArt[
+            sizeof("-XMadviseWillNeedArtFileSize:")-1 + PROPERTY_VALUE_MAX];
     char gctypeOptsBuf[sizeof("-Xgc:")-1 + PROPERTY_VALUE_MAX];
     char backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1 + PROPERTY_VALUE_MAX];
     char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX];
@@ -838,6 +844,22 @@
     parseRuntimeOption("dalvik.vm.madvise-random", madviseRandomOptsBuf, "-XX:MadviseRandomAccess:");
 
     /*
+     * Use default platform configuration as limits for madvising,
+     * when no properties are specified.
+     */
+    parseRuntimeOption("dalvik.vm.madvise.vdexfile.size",
+                       madviseWillNeedFileSizeVdex,
+                       "-XMadviseWillNeedVdexFileSize:");
+
+    parseRuntimeOption("dalvik.vm.madvise.odexfile.size",
+                       madviseWillNeedFileSizeOdex,
+                       "-XMadviseWillNeedOdexFileSize:");
+
+    parseRuntimeOption("dalvik.vm.madvise.artfile.size",
+                       madviseWillNeedFileSizeArt,
+                       "-XMadviseWillNeedArtFileSize:");
+
+    /*
      * Profile related options.
      */
     parseRuntimeOption("dalvik.vm.hot-startup-method-samples", hotstartupsamplesOptsBuf,
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 065c79b..452f55a 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -1419,6 +1419,42 @@
     return nativeToJavaStatus(status);
 }
 
+static jint android_media_AudioTrack_getStartThresholdInFrames(JNIEnv *env, jobject thiz) {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == nullptr) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                          "Unable to retrieve AudioTrack pointer for getStartThresholdInFrames()");
+        return (jint)AUDIO_JAVA_ERROR;
+    }
+    const ssize_t result = lpTrack->getStartThresholdInFrames();
+    if (result <= 0) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+                             "Internal error detected in getStartThresholdInFrames() = %zd",
+                             result);
+        return (jint)AUDIO_JAVA_ERROR;
+    }
+    return (jint)result; // this should be a positive value.
+}
+
+static jint android_media_AudioTrack_setStartThresholdInFrames(JNIEnv *env, jobject thiz,
+                                                               jint startThresholdInFrames) {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == nullptr) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                          "Unable to retrieve AudioTrack pointer for setStartThresholdInFrames()");
+        return (jint)AUDIO_JAVA_ERROR;
+    }
+    // non-positive values of startThresholdInFrames are not allowed by the Java layer.
+    const ssize_t result = lpTrack->setStartThresholdInFrames(startThresholdInFrames);
+    if (result <= 0) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+                             "Internal error detected in setStartThresholdInFrames() = %zd",
+                             result);
+        return (jint)AUDIO_JAVA_ERROR;
+    }
+    return (jint)result; // this should be a positive value.
+}
+
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 static const JNINativeMethod gMethods[] = {
@@ -1496,6 +1532,10 @@
          (void *)android_media_AudioTrack_getAudioDescriptionMixLeveldB},
         {"native_set_dual_mono_mode", "(I)I", (void *)android_media_AudioTrack_setDualMonoMode},
         {"native_get_dual_mono_mode", "([I)I", (void *)android_media_AudioTrack_getDualMonoMode},
+        {"native_setStartThresholdInFrames", "(I)I",
+         (void *)android_media_AudioTrack_setStartThresholdInFrames},
+        {"native_getStartThresholdInFrames", "()I",
+         (void *)android_media_AudioTrack_getStartThresholdInFrames},
 };
 
 // field names found in android/media/AudioTrack.java
diff --git a/core/jni/android_net_NetworkUtils.cpp b/core/jni/android_net_NetworkUtils.cpp
index 7508108..1cee895 100644
--- a/core/jni/android_net_NetworkUtils.cpp
+++ b/core/jni/android_net_NetworkUtils.cpp
@@ -52,27 +52,6 @@
 // FrameworkListener limits the size of commands to 4096 bytes.
 constexpr int MAXCMDSIZE = 4096;
 
-static void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
-    ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
-    if (detailMessage.get() == NULL) {
-        // Not really much we can do here. We're probably dead in the water,
-        // but let's try to stumble on...
-        env->ExceptionClear();
-    }
-    static jclass errnoExceptionClass =
-            MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/system/ErrnoException"));
-
-    static jmethodID errnoExceptionCtor =
-            GetMethodIDOrDie(env, errnoExceptionClass,
-            "<init>", "(Ljava/lang/String;I)V");
-
-    jobject exception = env->NewObject(errnoExceptionClass,
-                                       errnoExceptionCtor,
-                                       detailMessage.get(),
-                                       error);
-    env->Throw(reinterpret_cast<jthrowable>(exception));
-}
-
 static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
 {
     struct sock_filter filter_code[] = {
@@ -123,11 +102,6 @@
     return setNetworkForSocket(netId, AFileDescriptor_getFD(env, javaFd));
 }
 
-static jboolean android_net_utils_queryUserAccess(JNIEnv *env, jobject thiz, jint uid, jint netId)
-{
-    return (jboolean) !queryUserAccess(uid, netId);
-}
-
 static bool checkLenAndCopy(JNIEnv* env, const jbyteArray& addr, int len, void* dst)
 {
     if (env->GetArrayLength(addr) != len) {
@@ -150,7 +124,7 @@
     int fd = resNetworkQuery(netId, queryname.data(), ns_class, ns_type, flags);
 
     if (fd < 0) {
-        throwErrnoException(env, "resNetworkQuery", -fd);
+        jniThrowErrnoException(env, "resNetworkQuery", -fd);
         return nullptr;
     }
 
@@ -165,7 +139,7 @@
     int fd = resNetworkSend(netId, data, msgLen, flags);
 
     if (fd < 0) {
-        throwErrnoException(env, "resNetworkSend", -fd);
+        jniThrowErrnoException(env, "resNetworkSend", -fd);
         return nullptr;
     }
 
@@ -180,13 +154,13 @@
     int res = resNetworkResult(fd, &rcode, buf.data(), MAXPACKETSIZE);
     jniSetFileDescriptorOfFD(env, javaFd, -1);
     if (res < 0) {
-        throwErrnoException(env, "resNetworkResult", -res);
+        jniThrowErrnoException(env, "resNetworkResult", -res);
         return nullptr;
     }
 
     jbyteArray answer = env->NewByteArray(res);
     if (answer == nullptr) {
-        throwErrnoException(env, "resNetworkResult", ENOMEM);
+        jniThrowErrnoException(env, "resNetworkResult", ENOMEM);
         return nullptr;
     } else {
         env->SetByteArrayRegion(answer, 0, res,
@@ -208,7 +182,7 @@
 static jobject android_net_utils_getDnsNetwork(JNIEnv *env, jobject thiz) {
     unsigned dnsNetId = 0;
     if (int res = getNetworkForDns(&dnsNetId) < 0) {
-        throwErrnoException(env, "getDnsNetId", -res);
+        jniThrowErrnoException(env, "getDnsNetId", -res);
         return nullptr;
     }
     bool privateDnsBypass = dnsNetId & NETID_USE_LOCAL_NAMESERVERS;
@@ -233,8 +207,8 @@
     // Obtain the parameters of the TCP repair window.
     int rc = getsockopt(fd, IPPROTO_TCP, TCP_REPAIR_WINDOW, &trw, &size);
     if (rc == -1) {
-      throwErrnoException(env, "getsockopt : TCP_REPAIR_WINDOW", errno);
-      return NULL;
+        jniThrowErrnoException(env, "getsockopt : TCP_REPAIR_WINDOW", errno);
+        return NULL;
     }
 
     struct tcp_info tcpinfo = {};
@@ -244,8 +218,8 @@
     // should be applied to the window size.
     rc = getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcpinfo, &tcpinfo_size);
     if (rc == -1) {
-      throwErrnoException(env, "getsockopt : TCP_INFO", errno);
-      return NULL;
+        jniThrowErrnoException(env, "getsockopt : TCP_INFO", errno);
+        return NULL;
     }
 
     jclass class_TcpRepairWindow = env->FindClass("android/net/TcpRepairWindow");
@@ -267,7 +241,6 @@
     { "getBoundNetworkForProcess", "()I", (void*) android_net_utils_getBoundNetworkForProcess },
     { "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
     { "bindSocketToNetwork", "(Ljava/io/FileDescriptor;I)I", (void*) android_net_utils_bindSocketToNetwork },
-    { "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
     { "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter },
     { "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter },
     { "getTcpRepairWindow", "(Ljava/io/FileDescriptor;)Landroid/net/TcpRepairWindow;", (void*) android_net_utils_getTcpRepairWindow },
diff --git a/core/jni/android_os_SharedMemory.cpp b/core/jni/android_os_SharedMemory.cpp
index dc86187..fe375f4 100644
--- a/core/jni/android_os_SharedMemory.cpp
+++ b/core/jni/android_os_SharedMemory.cpp
@@ -35,21 +35,6 @@
 jclass errnoExceptionClass;
 jmethodID errnoExceptionCtor;  // MethodID for ErrnoException.<init>(String,I)
 
-void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
-    ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
-    if (detailMessage.get() == NULL) {
-        // Not really much we can do here. We're probably dead in the water,
-        // but let's try to stumble on...
-        env->ExceptionClear();
-    }
-
-    jobject exception = env->NewObject(errnoExceptionClass,
-                                       errnoExceptionCtor,
-                                       detailMessage.get(),
-                                       error);
-    env->Throw(reinterpret_cast<jthrowable>(exception));
-}
-
 jobject SharedMemory_nCreate(JNIEnv* env, jobject, jstring jname, jint size) {
 
     // Name is optional so we can't use ScopedUtfChars for this as it throws NPE on null
@@ -65,7 +50,7 @@
     }
 
     if (fd < 0) {
-        throwErrnoException(env, "SharedMemory_create", err);
+        jniThrowErrnoException(env, "SharedMemory_create", err);
         return nullptr;
     }
 
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index f71b42c..e4dddd2 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -486,9 +486,15 @@
     }
 
     void markVintf() {
+        AutoMutex _l(mLock);
         mVintf = true;
     }
 
+    void forceDowngradeToSystemStability() {
+        AutoMutex _l(mLock);
+        mVintf = false;
+    }
+
     sp<IBinder> getExtension() {
         AutoMutex _l(mLock);
         sp<JavaBBinder> b = mBinder.promote();
@@ -1013,6 +1019,12 @@
     jbh->markVintf();
 }
 
+static void android_os_Binder_forceDowngradeToSystemStability(JNIEnv* env, jobject clazz) {
+    JavaBBinderHolder* jbh =
+        (JavaBBinderHolder*) env->GetLongField(clazz, gBinderOffsets.mObject);
+    jbh->forceDowngradeToSystemStability();
+}
+
 static void android_os_Binder_flushPendingCommands(JNIEnv* env, jobject clazz)
 {
     IPCThreadState::self()->flushCommands();
@@ -1076,6 +1088,7 @@
     { "clearCallingWorkSource", "()J", (void*)android_os_Binder_clearCallingWorkSource },
     { "restoreCallingWorkSource", "(J)V", (void*)android_os_Binder_restoreCallingWorkSource },
     { "markVintfStability", "()V", (void*)android_os_Binder_markVintfStability},
+    { "forceDowngradeToSystemStability", "()V", (void*)android_os_Binder_forceDowngradeToSystemStability},
     { "flushPendingCommands", "()V", (void*)android_os_Binder_flushPendingCommands },
     { "getNativeBBinderHolder", "()J", (void*)android_os_Binder_getNativeBBinderHolder },
     { "getNativeFinalizer", "()J", (void*)android_os_Binder_getNativeFinalizer },
@@ -1516,7 +1529,9 @@
             res = JNI_TRUE;
         } else {
             jniThrowException(env, "java/util/NoSuchElementException",
-                              "Death link does not exist");
+                              base::StringPrintf("Death link does not exist (%s)",
+                                                 statusToString(err).c_str())
+                                      .c_str());
         }
     }
 
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 6becb07..6374305 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -52,6 +52,7 @@
 #include <string.h>
 #include <sys/epoll.h>
 #include <sys/errno.h>
+#include <sys/pidfd.h>
 #include <sys/resource.h>
 #include <sys/stat.h>
 #include <sys/syscall.h>
@@ -1290,34 +1291,10 @@
     return removeAllProcessGroups();
 }
 
-static void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
-    ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
-    if (detailMessage.get() == NULL) {
-        // Not really much we can do here. We're probably dead in the water,
-        // but let's try to stumble on...
-        env->ExceptionClear();
-    }
-    static jclass errnoExceptionClass =
-            MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/system/ErrnoException"));
-
-    static jmethodID errnoExceptionCtor =
-            GetMethodIDOrDie(env, errnoExceptionClass, "<init>", "(Ljava/lang/String;I)V");
-
-    jobject exception =
-            env->NewObject(errnoExceptionClass, errnoExceptionCtor, detailMessage.get(), error);
-    env->Throw(reinterpret_cast<jthrowable>(exception));
-}
-
-// Wrapper function to the syscall pidfd_open, which creates a file
-// descriptor that refers to the process whose PID is specified in pid.
-static inline int sys_pidfd_open(pid_t pid, unsigned int flags) {
-    return syscall(__NR_pidfd_open, pid, flags);
-}
-
 static jint android_os_Process_nativePidFdOpen(JNIEnv* env, jobject, jint pid, jint flags) {
-    int fd = sys_pidfd_open(pid, flags);
+    int fd = pidfd_open(pid, flags);
     if (fd < 0) {
-        throwErrnoException(env, "nativePidFdOpen", errno);
+        jniThrowErrnoException(env, "nativePidFdOpen", errno);
         return -1;
     }
     return fd;
diff --git a/core/proto/android/app/OWNERS b/core/proto/android/app/OWNERS
index 296abd1..cc479e6 100644
--- a/core/proto/android/app/OWNERS
+++ b/core/proto/android/app/OWNERS
@@ -1 +1 @@
-per-file location_time_zone_manager.proto = nfuller@google.com, mingaleev@google.com
+per-file location_time_zone_manager.proto, time_zone_detector.proto = nfuller@google.com, mingaleev@google.com
diff --git a/core/proto/android/app/location_time_zone_manager.proto b/core/proto/android/app/location_time_zone_manager.proto
index f44d549..891e9fc 100644
--- a/core/proto/android/app/location_time_zone_manager.proto
+++ b/core/proto/android/app/location_time_zone_manager.proto
@@ -17,6 +17,7 @@
 syntax = "proto2";
 package android.app.time;
 
+import "frameworks/base/core/proto/android/app/time_zone_detector.proto";
 import "frameworks/base/core/proto/android/privacy.proto";
 
 option java_multiple_files = true;
@@ -31,15 +32,6 @@
   repeated TimeZoneProviderStateProto secondary_provider_states = 3;
 }
 
-// Represents a GeolocationTimeZoneSuggestion that can be / has been passed to the time zone
-// detector.
-message GeolocationTimeZoneSuggestionProto {
-  option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
-  repeated string zone_ids = 1;
-  repeated string debug_info = 2;
-}
-
 // The state tracked for a LocationTimeZoneProvider.
 message TimeZoneProviderStateProto {
   option (android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/proto/android/app/time_zone_detector.proto b/core/proto/android/app/time_zone_detector.proto
new file mode 100644
index 0000000..b33ca1d
--- /dev/null
+++ b/core/proto/android/app/time_zone_detector.proto
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package android.app.time;
+
+import "frameworks/base/core/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+option java_outer_classname = "TimeZoneDetectorProto";
+
+// Represents a GeolocationTimeZoneSuggestion that can be / has been passed to the time zone
+// detector.
+message GeolocationTimeZoneSuggestionProto {
+  option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  repeated string zone_ids = 1;
+  repeated string debug_info = 2;
+}
+
+/*
+ * An obfuscated and simplified time zone suggestion for metrics use.
+ *
+ * The suggestion's time zone IDs (which relate to location) are obfuscated by
+ * mapping them to an ordinal. When the ordinal is assigned consistently across
+ * several objects (i.e. so the same time zone ID is always mapped to the same
+ * ordinal), this allows comparisons between those objects. For example, we can
+ * answer "did these two suggestions agree?", "does the suggestion match the
+ * device's current time zone?", without leaking knowledge of location. Ordinals
+ * are also significantly more compact than full IANA TZDB IDs, albeit highly
+ * unstable and of limited use.
+ */
+message MetricsTimeZoneSuggestion {
+  option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  enum Type {
+    CERTAIN = 1;
+    UNCERTAIN = 2;
+  }
+  optional Type type = 1;
+
+  // The ordinals for time zone(s) in the suggestion. Always empty for
+  // UNCERTAIN, and can be empty for CERTAIN, for example when the device is in
+  // a disputed area / on an ocean.
+  repeated uint32 time_zone_ordinals = 2;
+}
diff --git a/core/proto/android/net/networkcapabilities.proto b/core/proto/android/net/networkcapabilities.proto
new file mode 100644
index 0000000..edb6c04
--- /dev/null
+++ b/core/proto/android/net/networkcapabilities.proto
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+package android.net;
+
+option java_multiple_files = true;
+
+import "frameworks/base/core/proto/android/privacy.proto";
+import "frameworks/proto_logging/stats/enums/net/enums.proto";
+
+/**
+ * An android.net.NetworkCapabilities object.
+ */
+message NetworkCapabilitiesProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    repeated Transport transports = 1;
+
+    enum NetCapability {
+        // Indicates this is a network that has the ability to reach the
+        // carrier's MMSC for sending and receiving MMS messages.
+        NET_CAPABILITY_MMS = 0;
+        // Indicates this is a network that has the ability to reach the
+        // carrier's SUPL server, used to retrieve GPS information.
+        NET_CAPABILITY_SUPL = 1;
+        // Indicates this is a network that has the ability to reach the
+        // carrier's DUN or tethering gateway.
+        NET_CAPABILITY_DUN = 2;
+        // Indicates this is a network that has the ability to reach the
+        // carrier's FOTA portal, used for over the air updates.
+        NET_CAPABILITY_FOTA = 3;
+        // Indicates this is a network that has the ability to reach the
+        // carrier's IMS servers, used for network registration and signaling.
+        NET_CAPABILITY_IMS = 4;
+        // Indicates this is a network that has the ability to reach the
+        // carrier's CBS servers, used for carrier specific services.
+        NET_CAPABILITY_CBS = 5;
+        // Indicates this is a network that has the ability to reach a Wi-Fi
+        // direct peer.
+        NET_CAPABILITY_WIFI_P2P = 6;
+        // Indicates this is a network that has the ability to reach a carrier's
+        // Initial Attach servers.
+        NET_CAPABILITY_IA = 7;
+        // Indicates this is a network that has the ability to reach a carrier's
+        // RCS servers, used for Rich Communication Services.
+        NET_CAPABILITY_RCS = 8;
+        // Indicates this is a network that has the ability to reach a carrier's
+        // XCAP servers, used for configuration and control.
+        NET_CAPABILITY_XCAP = 9;
+        // Indicates this is a network that has the ability to reach a carrier's
+        // Emergency IMS servers or other services, used for network signaling
+        // during emergency calls.
+        NET_CAPABILITY_EIMS = 10;
+        // Indicates that this network is unmetered.
+        NET_CAPABILITY_NOT_METERED = 11;
+        // Indicates that this network should be able to reach the internet.
+        NET_CAPABILITY_INTERNET = 12;
+        // Indicates that this network is available for general use. If this is
+        // not set applications should not attempt to communicate on this
+        // network. Note that this is simply informative and not enforcement -
+        // enforcement is handled via other means. Set by default.
+        NET_CAPABILITY_NOT_RESTRICTED = 13;
+        // Indicates that the user has indicated implicit trust of this network.
+        // This generally means it's a sim-selected carrier, a plugged in
+        // ethernet, a paired BT device or a wifi the user asked to connect to.
+        // Untrusted networks are probably limited to unknown wifi AP. Set by
+        // default.
+        NET_CAPABILITY_TRUSTED = 14;
+        // Indicates that this network is not a VPN.  This capability is set by
+        // default and should be explicitly cleared for VPN networks.
+        NET_CAPABILITY_NOT_VPN = 15;
+        // Indicates that connectivity on this network was successfully
+        // validated. For example, for a network with NET_CAPABILITY_INTERNET,
+        // it means that Internet connectivity was successfully detected.
+        NET_CAPABILITY_VALIDATED = 16;
+        // Indicates that this network was found to have a captive portal in
+        // place last time it was probed.
+        NET_CAPABILITY_CAPTIVE_PORTAL = 17;
+        // Indicates that this network is not roaming.
+        NET_CAPABILITY_NOT_ROAMING = 18;
+        // Indicates that this network is available for use by apps, and not a
+        // network that is being kept up in the background to facilitate fast
+        // network switching.
+        NET_CAPABILITY_FOREGROUND = 19;
+    }
+    repeated NetCapability capabilities = 2;
+
+    // Passive link bandwidth. This is a rough guide of the expected peak
+    // bandwidth for the first hop on the given transport.  It is not measured,
+    // but may take into account link parameters (Radio technology, allocated
+    // channels, etc).
+    optional int32 link_up_bandwidth_kbps = 3;
+    optional int32 link_down_bandwidth_kbps = 4;
+
+    optional string network_specifier = 5 [ (.android.privacy).dest = DEST_EXPLICIT ];
+
+    // True if this object specifies a signal strength.
+    optional bool can_report_signal_strength = 6;
+    // This is a signed integer, and higher values indicate better signal. The
+    // exact units are bearer-dependent. For example, Wi-Fi uses RSSI.
+    // Only valid if can_report_signal_strength is true.
+    optional sint32 signal_strength = 7;
+}
diff --git a/core/proto/android/net/networkrequest.proto b/core/proto/android/net/networkrequest.proto
index 0041f19..57b9f71 100644
--- a/core/proto/android/net/networkrequest.proto
+++ b/core/proto/android/net/networkrequest.proto
@@ -20,8 +20,8 @@
 
 option java_multiple_files = true;
 
+import "frameworks/base/core/proto/android/net/networkcapabilities.proto";
 import "frameworks/base/core/proto/android/privacy.proto";
-import "frameworks/proto_logging/stats/enums/net/networkcapabilities.proto";
 
 /**
  * An android.net.NetworkRequest object.
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index e97b1a8..64cf75d 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -55,13 +55,13 @@
 import "frameworks/base/core/proto/android/service/procstats.proto";
 import "frameworks/base/core/proto/android/service/restricted_image.proto";
 import "frameworks/base/core/proto/android/service/sensor_service.proto";
+import "frameworks/base/core/proto/android/service/usb.proto";
 import "frameworks/base/core/proto/android/util/event_log_tags.proto";
 import "frameworks/base/core/proto/android/util/log.proto";
 import "frameworks/base/core/proto/android/util/textdump.proto";
 import "frameworks/base/core/proto/android/privacy.proto";
 import "frameworks/base/core/proto/android/section.proto";
 import "frameworks/base/proto/src/ipconnectivity.proto";
-import "frameworks/proto_logging/stats/enums/service/usb.proto";
 
 package android.os;
 
diff --git a/core/proto/android/service/enums.proto b/core/proto/android/service/enums.proto
deleted file mode 100644
index b64e685..0000000
--- a/core/proto/android/service/enums.proto
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.
- */
-
-syntax = "proto2";
-package android.service;
-
-option java_outer_classname = "ServiceProtoEnums";
-option java_multiple_files = true;
-
-enum UsbEndPointType {
-    USB_ENDPOINT_TYPE_XFER_CONTROL = 0;
-    USB_ENDPOINT_TYPE_XFER_ISOC = 1;
-    USB_ENDPOINT_TYPE_XFER_BULK = 2;
-    USB_ENDPOINT_TYPE_XFER_INT = 3;
-}
-
-enum UsbEndPointDirection {
-    USB_ENDPOINT_DIR_OUT = 0;
-    USB_ENDPOINT_DIR_IN = 0x80;
-}
-
-enum UsbConnectionRecordMode {
-    USB_CONNECTION_RECORD_MODE_CONNECT = 0;
-    USB_CONNECTION_RECORD_MODE_CONNECT_BADPARSE = 1;
-    USB_CONNECTION_RECORD_MODE_CONNECT_BADDEVICE = 2;
-    USB_CONNECTION_RECORD_MODE_DISCONNECT = -1;
-}
\ No newline at end of file
diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto
new file mode 100644
index 0000000..dd313aa
--- /dev/null
+++ b/core/proto/android/service/usb.proto
@@ -0,0 +1,431 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package android.service.usb;
+
+option java_multiple_files = true;
+option java_outer_classname = "UsbServiceProto";
+
+import "frameworks/base/core/proto/android/content/component_name.proto";
+import "frameworks/base/core/proto/android/privacy.proto";
+import "frameworks/proto_logging/stats/enums/service/enums.proto";
+
+message UsbServiceDumpProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional UsbDeviceManagerProto device_manager = 1;
+    optional UsbHostManagerProto host_manager = 2;
+    optional UsbPortManagerProto port_manager = 3;
+    optional UsbAlsaManagerProto alsa_manager = 4;
+    optional UsbSettingsManagerProto settings_manager = 5;
+    optional UsbPermissionsManagerProto permissions_manager = 6;
+}
+
+message UsbDeviceManagerProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional UsbHandlerProto handler = 1;
+    optional UsbDebuggingManagerProto debugging_manager = 2;
+}
+
+message UsbHandlerProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    /* Same as android.hardware.usb.gadget.V1_0.GadgetFunction.* */
+    enum Function {
+        FUNCTION_ADB = 1;
+        FUNCTION_ACCESSORY = 2;
+        FUNCTION_MTP = 4;
+        FUNCTION_MIDI = 8;
+        FUNCTION_PTP = 16;
+        FUNCTION_RNDIS = 32;
+        FUNCTION_AUDIO_SOURCE = 64;
+    }
+
+    repeated Function current_functions = 1;
+    optional bool current_functions_applied = 2;
+    repeated Function screen_unlocked_functions = 3;
+    optional bool screen_locked = 4;
+    optional bool connected = 5;
+    optional bool configured = 6;
+    optional UsbAccessoryProto current_accessory = 7;
+    optional bool host_connected = 8;
+    optional bool source_power = 9;
+    optional bool sink_power = 10;
+    optional bool usb_charging = 11;
+    optional bool hide_usb_notification = 12;
+    optional bool audio_accessory_connected = 13;
+    optional bool adb_enabled = 14;
+    optional string kernel_state = 15;
+    optional string kernel_function_list = 16;
+}
+
+message UsbAccessoryProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional string manufacturer = 1;
+    optional string model = 2;
+    // For "classical" USB-accessories the manufacturer bakes this into the
+    // firmware of the device. If an Android phone is configured as accessory, the
+    // app that sets up the accessory side of the connection set this. Either way,
+    // these are part of the detection protocol, and so they cannot be user set or
+    // unique.
+    optional string description = 3;
+    optional string version = 4;
+    optional string uri = 5 [ (android.privacy).dest = DEST_EXPLICIT ];
+    // Non-resettable hardware ID.
+    optional string serial = 6 [ (android.privacy).dest = DEST_LOCAL ];
+}
+
+message UsbDebuggingManagerProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional bool connected_to_adb = 1;
+    // A workstation that connects to the phone for debugging is identified by
+    // this key.
+    optional string last_key_received = 2 [ (android.privacy).dest = DEST_EXPLICIT ];
+    optional string user_keys = 3 [ (android.privacy).dest = DEST_LOCAL ];
+    optional string system_keys = 4 [ (android.privacy).dest = DEST_LOCAL ];
+}
+
+message UsbHostManagerProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional android.content.ComponentNameProto default_usb_host_connection_handler = 1;
+    repeated UsbDeviceProto devices = 2;
+    optional int32 num_connects = 3;
+    repeated UsbConnectionRecordProto connections = 4;
+}
+
+message UsbDeviceProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    // Generic USB name, not user-provided.
+    optional string name = 1;
+    // ID specific to the vendor, not the device.
+    optional int32 vendor_id = 2;
+    // ID of this product type: Each vendor gives each product a unique ID. E.g.
+    // all mice of the same model would have the same ID.
+    optional int32 product_id = 3;
+    optional int32 class = 4;
+    optional int32 subclass = 5;
+    optional int32 protocol = 6;
+    optional string manufacturer_name = 7;
+    optional string product_name = 8;
+    optional string version = 9;
+    // Non-resettable hardware ID.
+    optional string serial_number = 10 [ (android.privacy).dest = DEST_LOCAL ];
+    repeated UsbConfigurationProto configurations = 11;
+}
+
+message UsbConfigurationProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    // A single USB device can have several configurations and the app accessing
+    // the USB device can switch between them. At any time only one can be active.
+    // Each configuration can present completely different interfaces end
+    // endpoints, i.e. a completely different behavior.
+    optional int32 id = 1;
+    // Hardware-defined name, not set by the user.
+    optional string name = 2;
+    optional uint32 attributes = 3;
+    optional int32 max_power = 4;
+    repeated UsbInterfaceProto interfaces = 5;
+}
+
+message UsbInterfaceProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    // Hardware defined. This is the id used by the app to identify the interface.
+    optional int32 id = 1;
+    optional int32 alternate_settings = 2;
+    optional string name = 3;
+    optional int32 class = 4;
+    optional int32 subclass = 5;
+    optional int32 protocol = 6;
+    repeated UsbEndPointProto endpoints = 7;
+}
+
+message UsbEndPointProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional int32 endpoint_number = 1;
+    optional android.service.UsbEndPointDirection direction = 2;
+      // The address of the endpoint. Needed to read and write to the endpoint.
+    optional int32 address = 3;
+    optional android.service.UsbEndPointType type = 4;
+    optional uint32 attributes = 5;
+    optional int32 max_packet_size = 6;
+    optional int32 interval = 7;
+}
+
+message UsbConnectionRecordProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    // usb device's address, e.g. 001/002, nothing about the phone
+    optional string device_address = 1;
+    optional android.service.UsbConnectionRecordMode mode = 2;
+    optional int64 timestamp = 3;
+    optional int32 manufacturer = 4;
+    optional int32 product = 5;
+    optional UsbIsHeadsetProto is_headset = 6;
+}
+
+message UsbIsHeadsetProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional bool in = 1;
+    optional bool out = 2;
+}
+
+message UsbPortManagerProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional bool is_simulation_active = 1;
+    repeated UsbPortInfoProto usb_ports = 2;
+}
+
+message UsbPortInfoProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional UsbPortProto port = 1;
+    optional UsbPortStatusProto status = 2;
+    optional bool can_change_mode = 3;
+    optional bool can_change_power_role = 4;
+    optional bool can_change_data_role = 5;
+    optional int64 connected_at_millis = 6;
+    optional int64 last_connect_duration_millis = 7;
+}
+
+message UsbPortProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    /* Same as android.hardware.usb.V1_1.Constants.PortMode_1_1 */
+    enum Mode {
+        MODE_NONE = 0;
+        MODE_UFP = 1;
+        MODE_DFP = 2;
+        MODE_DRP = 3;
+        MODE_AUDIO_ACCESSORY = 4;
+        MODE_DEBUG_ACCESSORY = 8;
+    }
+
+    // ID of the port. A device (eg: Chromebooks) might have multiple ports.
+    optional string id = 1;
+    repeated Mode supported_modes = 2;
+}
+
+message UsbPortStatusProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    /* Same as android.hardware.usb.V1_0.Constants.PortPowerRole */
+    enum PowerRole {
+        POWER_ROLE_NONE = 0;
+        POWER_ROLE_SOURCE = 1;
+        POWER_ROLE_SINK = 2;
+    }
+
+    /* Same as android.hardware.usb.V1_0.Constants.PortDataRole */
+    enum DataRole {
+        DATA_ROLE_NONE = 0;
+        DATA_ROLE_HOST = 1;
+        DATA_ROLE_DEVICE = 2;
+    }
+
+    optional bool connected = 1;
+    optional UsbPortProto.Mode current_mode = 2;
+    optional PowerRole power_role = 3;
+    optional DataRole data_role = 4;
+    repeated UsbPortStatusRoleCombinationProto role_combinations = 5;
+    optional android.service.ContaminantPresenceStatus contaminant_presence_status = 6;
+}
+
+message UsbPortStatusRoleCombinationProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional UsbPortStatusProto.PowerRole power_role = 1;
+    optional UsbPortStatusProto.DataRole data_role = 2;
+}
+
+message UsbAlsaManagerProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional int32 cards_parser = 1;
+    repeated UsbAlsaDeviceProto alsa_devices = 2;
+    repeated UsbMidiDeviceProto midi_devices = 3;
+}
+
+message UsbAlsaDeviceProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional int32 card = 1;
+    optional int32 device = 2;
+    optional string name = 3;
+    optional bool has_playback = 4;
+    optional bool has_capture = 5;
+    // usb device's address, e.g. 001/002, nothing about the phone
+    optional string address = 6;
+}
+
+message UsbMidiDeviceProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional int32 card = 1;
+    optional int32 device = 2;
+    // usb device's address, e.g. 001/002, nothing about the phone
+    optional string device_address = 3;
+}
+
+message UsbSettingsManagerProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    repeated UsbUserSettingsManagerProto user_settings = 1;
+    repeated UsbProfileGroupSettingsManagerProto profile_group_settings = 2;
+}
+
+message UsbUserSettingsManagerProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional int32 user_id = 1;
+    reserved 2; // previously device_permissions, now unused
+    reserved 3; // previously accessory_permissions, now unused
+    repeated UsbDeviceAttachedActivities device_attached_activities = 4;
+    repeated UsbAccessoryAttachedActivities accessory_attached_activities = 5;
+}
+
+message UsbProfileGroupSettingsManagerProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    // The user id of the personal profile if the device has a work profile.
+    optional int32 parent_user_id = 1;
+    repeated UsbSettingsDevicePreferenceProto device_preferences = 2;
+    repeated UsbSettingsAccessoryPreferenceProto accessory_preferences = 3;
+}
+
+message UsbSettingsDevicePreferenceProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional UsbDeviceFilterProto filter = 1;
+    optional UserPackageProto user_package = 2;
+}
+
+message UsbPermissionsManagerProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    repeated UsbUserPermissionsManagerProto user_permissions = 1;
+}
+
+message UsbUserPermissionsManagerProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional int32 user_id = 1;
+
+    repeated UsbDevicePermissionProto device_permissions = 2;
+    repeated UsbAccessoryPermissionProto accessory_permissions = 3;
+
+    repeated UsbDevicePersistentPermissionProto device_persistent_permissions = 4;
+    repeated UsbAccessoryPersistentPermissionProto accessory_persistent_permissions = 5;
+}
+
+message UsbDevicePermissionProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    // Name of device set by manufacturer
+    // All devices of the same model have the same name
+    optional string device_name = 1;
+    repeated int32 uids = 2;
+}
+
+message UsbAccessoryPermissionProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    // Description of accessory set by manufacturer
+    // All accessories of the same model have the same description
+    optional string accessory_description = 1;
+    repeated int32 uids = 2;
+}
+
+message UsbDevicePersistentPermissionProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional UsbDeviceFilterProto device_filter = 1;
+    repeated UsbUidPermissionProto permission_values = 2;
+}
+
+message UsbAccessoryPersistentPermissionProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional UsbAccessoryFilterProto accessory_filter = 1;
+    repeated UsbUidPermissionProto permission_values = 2;
+}
+
+message UsbUidPermissionProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional int32 uid = 1;
+    optional bool is_granted = 2;
+}
+
+message UsbDeviceFilterProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    // Mirrors the vendor_id of UsbDeviceProto.
+    optional int32 vendor_id = 1;
+    optional int32 product_id = 2;
+    optional int32 class = 3;
+    optional int32 subclass = 4;
+    optional int32 protocol = 5;
+    optional string manufacturer_name = 6;
+    optional string product_name = 7;
+    optional string serial_number = 8 [ (android.privacy).dest = DEST_EXPLICIT ];
+}
+
+message UserPackageProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional int32 user_id = 1;
+    optional string package_name =2;
+}
+
+message UsbSettingsAccessoryPreferenceProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional UsbAccessoryFilterProto filter = 1;
+    optional UserPackageProto user_package = 2;
+}
+
+message UsbAccessoryFilterProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional string manufacturer = 1;
+    optional string model = 2;
+    optional string version = 3;
+}
+
+message UsbDeviceAttachedActivities {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional android.content.ComponentNameProto activity = 1;
+    repeated UsbDeviceFilterProto filters = 2;
+}
+
+message UsbAccessoryAttachedActivities {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional android.content.ComponentNameProto activity = 1;
+    repeated UsbAccessoryFilterProto filters = 2;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2f352e9..f65d7a7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5123,9 +5123,16 @@
     <permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"
                 android:protectionLevel="signature|privileged" />
     <!-- Allows an app to override compat change config.
+         This permission only allows to override config on debuggable builds or test-apks and is
+         therefore a less powerful version of OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD.
          @hide  <p>Not for use by third-party applications.</p> -->
     <permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"
                 android:protectionLevel="signature|privileged" />
+    <!-- @SystemApi Allows an app to override compat change config on release builds.
+        Only ChangeIds that are annotated as @Overridable can be overridden on release builds.
+        @hide -->
+    <permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"
+                android:protectionLevel="signature|privileged" />
 
     <!-- Allows input events to be monitored. Very dangerous!  @hide -->
     <permission android:name="android.permission.MONITOR_INPUT"
@@ -5172,6 +5179,10 @@
     <permission android:name="android.permission.INPUT_CONSUMER"
                 android:protectionLevel="signature" />
 
+    <!-- @hide @SystemApi Allows an application to manage app hibernation state. -->
+    <permission android:name="android.permission.MANAGE_APP_HIBERNATION"
+                android:protectionLevel="signature|installer" />
+
     <!-- Attribution for Country Detector. -->
     <attribution android:tag="CountryDetector" android:label="@string/country_detector"/>
     <!-- Attribution for Location service. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 22a79ed..bfe7802 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -327,9 +327,6 @@
         <item>"0,1"</item>
     </string-array>
 
-    <!-- The maximum duration (in milliseconds) we expect a network transition to take -->
-    <integer name="config_networkTransitionTimeout">60000</integer>
-
     <!-- Whether/how to notify the user on network switches. See LingerMonitor.java. -->
     <integer translatable="false" name="config_networkNotifySwitchType">0</integer>
 
@@ -342,16 +339,6 @@
          Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. -->
     <integer translatable="false" name="config_networkAvoidBadWifi">1</integer>
 
-    <!-- Configuration hook for the URL returned by ConnectivityManager#getCaptivePortalServerUrl.
-         If empty, the returned value is controlled by Settings.Global.CAPTIVE_PORTAL_HTTP_URL,
-         and if that value is empty, the framework will use a hard-coded default.
-         This is *NOT* a URL that will always be used by the system network validation to detect
-         captive portals: NetworkMonitor may use different strategies and will not necessarily use
-         this URL. NetworkMonitor behaviour should be configured with NetworkStack resource overlays
-         instead. -->
-    <!--suppress CheckTagEmptyBody -->
-    <string translatable="false" name="config_networkCaptivePortalServerUrl"></string>
-
     <!-- If the hardware supports specially marking packets that caused a wakeup of the
          main CPU, set this value to the mark used. -->
     <integer name="config_networkWakeupPacketMark">0</integer>
@@ -459,14 +446,6 @@
          apps requested it. -->
     <bool name="config_vehicleInternalNetworkAlwaysRequested">false</bool>
 
-    <!-- Configuration of network interfaces that support WakeOnLAN -->
-    <string-array translatable="false" name="config_wakeonlan_supported_interfaces">
-        <!--
-        <item>wlan0</item>
-        <item>eth0</item>
-        -->
-    </string-array>
-
     <!-- This setting is deprecated, please use
          com.android.networkstack.tethering.R.array.config_mobile_hotspot_provision_app instead. -->
     <string-array translatable="false" name="config_mobile_hotspot_provision_app">
@@ -3436,10 +3415,6 @@
     <!-- True if assistant app should be pinned via Pinner Service -->
     <bool name="config_pinnerAssistantApp">false</bool>
 
-    <!-- List of files pinned by the Pinner Service with the JIT Zygote boot image b/119800099 -->
-    <string-array translatable="false" name="config_jitzygoteBootImagePinnerServiceFiles">
-    </string-array>
-
     <!-- Number of days preloaded file cache should be preserved on a device before it can be
          deleted -->
     <integer name="config_keepPreloadsMinDays">7</integer>
@@ -4364,6 +4339,11 @@
          check after reboot or airplane mode toggling -->
     <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">false</bool>
 
+    <!-- Boolean indicating whether all CB messages should be disabled on this device. This config
+         is intended to be used by OEMs who need to disable CB messages for regulatory requirements,
+         (e.g. the device is a tablet in a country where tablets should not receive CB messages) -->
+    <bool translatable="false" name="config_disable_all_cb_messages">false</bool>
+
     <!-- Screen Wake Keys
          Determines whether the specified key groups can be used to wake up the device. -->
     <bool name="config_wakeOnDpadKeyPress">true</bool>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8ac00dc..4a06671 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3605,6 +3605,8 @@
     <string name="ext_media_checking_notification_title">Checking <xliff:g id="name" example="SD card">%s</xliff:g>\u2026</string>
     <!-- Notification body when external media is being checked [CHAR LIMIT=NONE] -->
     <string name="ext_media_checking_notification_message">Reviewing current content</string>
+    <!-- TV specific notification body when external media is being checked [CHAR LIMIT=75] -->
+    <string name="ext_media_checking_notification_message" product="tv">Analyzing media storage</string>
 
     <!-- Notification body when new external media is detected [CHAR LIMIT=30] -->
     <string name="ext_media_new_notification_title">New <xliff:g id="name" example="SD card">%s</xliff:g></string>
@@ -3612,11 +3614,15 @@
     <string name="ext_media_new_notification_title" product="automotive"><xliff:g id="name" example="SD card">%s</xliff:g> isn\u2019t working</string>
     <!-- Notification body when new external media is detected [CHAR LIMIT=NONE] -->
     <string name="ext_media_new_notification_message">Tap to set up</string>
+    <!-- TV specific notification body when new external media is detected [CHAR LIMIT=75] -->
+    <string name="ext_media_new_notification_message" product="tv">Select to set up</string>
     <!-- Automotive specific notification body when new external media is detected. [CHAR LIMIT=NONE] -->
     <string name="ext_media_new_notification_message" product="automotive">You may need to reformat the device. Tap to eject.</string>
 
     <!-- Notification body when external media is ready for use [CHAR LIMIT=NONE] -->
     <string name="ext_media_ready_notification_message">For transferring photos and media</string>
+    <!-- TV specific notification body when external media is ready for use [CHAR LIMIT=75] -->
+    <string name="ext_media_ready_notification_message" product="tv">Browse media files</string>
 
     <!-- Notification title when external media is unmountable (corrupt) [CHAR LIMIT=30] -->
     <string name="ext_media_unmountable_notification_title">Issue with <xliff:g id="name" example="SD card">%s</xliff:g></string>
@@ -3635,8 +3641,8 @@
     <string name="ext_media_unsupported_notification_title" product="automotive"><xliff:g id="name" example="SD card">%s</xliff:g> isn\u2019t working</string>
     <!-- Notification body when external media is unsupported [CHAR LIMIT=NONE] -->
     <string name="ext_media_unsupported_notification_message">This device doesn\u2019t support this <xliff:g id="name" example="SD card">%s</xliff:g>. Tap to set up in a supported format.</string>
-    <!-- TV-specific notification body when external media is unsupported [CHAR LIMIT=NONE] -->
-    <string name="ext_media_unsupported_notification_message" product="tv">This device doesn\u2019t support this <xliff:g id="name" example="SD card">%s</xliff:g>. Select to set up in a supported format.</string>
+    <!-- TV-specific notification body when external media is unsupported [CHAR LIMIT=75] -->
+    <string name="ext_media_unsupported_notification_message" product="tv">Select to set up <xliff:g id="name" example="SD card">%s</xliff:g> in a supported format.</string>
     <!-- Automotive specific notification body when external media is unsupported [CHAR LIMIT=NONE] -->
     <string name="ext_media_unsupported_notification_message" product="automotive">You may need to reformat the device</string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index fc75463..2901de5 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -685,7 +685,6 @@
   <java-symbol type="string" name="not_checked" />
   <java-symbol type="array" name="config_ethernet_interfaces" />
   <java-symbol type="bool" name="config_vehicleInternalNetworkAlwaysRequested" />
-  <java-symbol type="array" name="config_wakeonlan_supported_interfaces" />
   <java-symbol type="string" name="config_forceVoiceInteractionServicePackage" />
   <java-symbol type="string" name="config_mms_user_agent" />
   <java-symbol type="string" name="config_mms_user_agent_profile_url" />
@@ -1951,11 +1950,9 @@
   <java-symbol type="integer" name="config_lowBatteryCloseWarningBump" />
   <java-symbol type="integer" name="config_lowBatteryWarningLevel" />
   <java-symbol type="integer" name="config_networkPolicyDefaultWarning" />
-  <java-symbol type="integer" name="config_networkTransitionTimeout" />
   <java-symbol type="integer" name="config_networkNotifySwitchType" />
   <java-symbol type="array" name="config_networkNotifySwitches" />
   <java-symbol type="integer" name="config_networkAvoidBadWifi" />
-  <java-symbol type="string" name="config_networkCaptivePortalServerUrl" />
   <java-symbol type="integer" name="config_networkWakeupPacketMark" />
   <java-symbol type="integer" name="config_networkWakeupPacketMask" />
   <java-symbol type="bool" name="config_apfDrop802_3Frames" />
@@ -3065,7 +3062,6 @@
   <java-symbol type="bool" name="config_pinnerCameraApp" />
   <java-symbol type="bool" name="config_pinnerHomeApp" />
   <java-symbol type="bool" name="config_pinnerAssistantApp" />
-  <java-symbol type="array" name="config_jitzygoteBootImagePinnerServiceFiles" />
 
   <java-symbol type="string" name="config_doubleTouchGestureEnableFile" />
 
@@ -3851,6 +3847,7 @@
   <java-symbol type="layout" name="chooser_action_button" />
   <java-symbol type="dimen" name="chooser_action_button_icon_size" />
   <java-symbol type="string" name="config_defaultNearbySharingComponent" />
+  <java-symbol type="bool" name="config_disable_all_cb_messages" />
   <java-symbol type="drawable" name="ic_close" />
 
   <java-symbol type="bool" name="config_automotiveHideNavBarForKeyboard" />
diff --git a/core/tests/coretests/src/android/content/OWNERS b/core/tests/coretests/src/android/content/OWNERS
index c61a4b5..0b94589 100644
--- a/core/tests/coretests/src/android/content/OWNERS
+++ b/core/tests/coretests/src/android/content/OWNERS
@@ -2,3 +2,5 @@
 per-file ContextTest.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS
 per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
+per-file ComponentCallbacksControllerTest = file:/services/core/java/com/android/server/wm/OWNERS
+per-file ComponentCallbacksControllerTest = charlesccchen@google.com
diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
index 2c1bbf0..aa2eb63 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -232,7 +232,7 @@
         assertThat(entry3.handlerClassName).isEqualTo(
                 "com.android.internal.os.LooperStatsTest$TestHandlerSecond");
         assertThat(entry3.messageName).startsWith(
-                "com.android.internal.os.LooperStatsTest-$$ExternalSyntheticLambda");
+                "com.android.internal.os.LooperStatsTest$$ExternalSyntheticLambda4");
         assertThat(entry3.messageCount).isEqualTo(1);
         assertThat(entry3.recordedMessageCount).isEqualTo(1);
         assertThat(entry3.exceptionCount).isEqualTo(0);
diff --git a/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java b/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java
deleted file mode 100644
index 7175f56..0000000
--- a/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.util;
-
-import static android.Manifest.permission.NETWORK_SETTINGS;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.Manifest;
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.location.LocationManager;
-import android.os.Binder;
-import android.os.Build;
-import android.os.UserHandle;
-import android.os.UserManager;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import java.util.HashMap;
-
-/** Unit tests for {@link LocationPermissionChecker}. */
-public class LocationPermissionCheckerTest {
-
-    public static final String TAG = "ConnectivityUtilTest";
-
-    // Mock objects for testing
-    @Mock private Context mMockContext;
-    @Mock private PackageManager mMockPkgMgr;
-    @Mock private ApplicationInfo mMockApplInfo;
-    @Mock private AppOpsManager mMockAppOps;
-    @Mock private UserManager mMockUserManager;
-    @Mock private LocationManager mLocationManager;
-
-    private static final String TEST_PKG_NAME = "com.google.somePackage";
-    private static final String TEST_FEATURE_ID = "com.google.someFeature";
-    private static final int MANAGED_PROFILE_UID = 1100000;
-    private static final int OTHER_USER_UID = 1200000;
-
-    private final String mInteractAcrossUsersFullPermission =
-            "android.permission.INTERACT_ACROSS_USERS_FULL";
-    private final String mManifestStringCoarse =
-            Manifest.permission.ACCESS_COARSE_LOCATION;
-    private final String mManifestStringFine =
-            Manifest.permission.ACCESS_FINE_LOCATION;
-
-    // Test variables
-    private int mWifiScanAllowApps;
-    private int mUid;
-    private int mCoarseLocationPermission;
-    private int mAllowCoarseLocationApps;
-    private int mFineLocationPermission;
-    private int mAllowFineLocationApps;
-    private int mNetworkSettingsPermission;
-    private int mCurrentUser;
-    private boolean mIsLocationEnabled;
-    private boolean mThrowSecurityException;
-    private Answer<Integer> mReturnPermission;
-    private HashMap<String, Integer> mPermissionsList = new HashMap<String, Integer>();
-    private LocationPermissionChecker mChecker;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        initTestVars();
-    }
-
-    private void setupMocks() throws Exception {
-        when(mMockPkgMgr.getApplicationInfoAsUser(eq(TEST_PKG_NAME), eq(0), any()))
-                .thenReturn(mMockApplInfo);
-        when(mMockContext.getPackageManager()).thenReturn(mMockPkgMgr);
-        when(mMockAppOps.noteOp(AppOpsManager.OPSTR_WIFI_SCAN, mUid, TEST_PKG_NAME,
-                TEST_FEATURE_ID, null)).thenReturn(mWifiScanAllowApps);
-        when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_COARSE_LOCATION), eq(mUid),
-                eq(TEST_PKG_NAME), eq(TEST_FEATURE_ID), nullable(String.class)))
-                .thenReturn(mAllowCoarseLocationApps);
-        when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_FINE_LOCATION), eq(mUid),
-                eq(TEST_PKG_NAME), eq(TEST_FEATURE_ID), nullable(String.class)))
-                .thenReturn(mAllowFineLocationApps);
-        if (mThrowSecurityException) {
-            doThrow(new SecurityException("Package " + TEST_PKG_NAME + " doesn't belong"
-                    + " to application bound to user " + mUid))
-                    .when(mMockAppOps).checkPackage(mUid, TEST_PKG_NAME);
-        }
-        when(mMockContext.getSystemService(Context.APP_OPS_SERVICE))
-                .thenReturn(mMockAppOps);
-        when(mMockContext.getSystemService(Context.USER_SERVICE))
-                .thenReturn(mMockUserManager);
-        when(mMockContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager);
-    }
-
-    private void setupTestCase() throws Exception {
-        setupMocks();
-        setupMockInterface();
-        mChecker = new LocationPermissionChecker(mMockContext);
-    }
-
-    private void initTestVars() {
-        mPermissionsList.clear();
-        mReturnPermission = createPermissionAnswer();
-        mWifiScanAllowApps = AppOpsManager.MODE_ERRORED;
-        mUid = OTHER_USER_UID;
-        mThrowSecurityException = true;
-        mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.M;
-        mIsLocationEnabled = false;
-        mCurrentUser = ActivityManager.getCurrentUser();
-        mCoarseLocationPermission = PackageManager.PERMISSION_DENIED;
-        mFineLocationPermission = PackageManager.PERMISSION_DENIED;
-        mAllowCoarseLocationApps = AppOpsManager.MODE_ERRORED;
-        mAllowFineLocationApps = AppOpsManager.MODE_ERRORED;
-        mNetworkSettingsPermission = PackageManager.PERMISSION_DENIED;
-    }
-
-    private void setupMockInterface() {
-        Binder.restoreCallingIdentity((((long) mUid) << 32) | Binder.getCallingPid());
-        doAnswer(mReturnPermission).when(mMockContext).checkPermission(
-                anyString(), anyInt(), anyInt());
-        when(mMockUserManager.isSameProfileGroup(UserHandle.SYSTEM,
-                UserHandle.getUserHandleForUid(MANAGED_PROFILE_UID)))
-                .thenReturn(true);
-        when(mMockContext.checkPermission(mManifestStringCoarse, -1, mUid))
-                .thenReturn(mCoarseLocationPermission);
-        when(mMockContext.checkPermission(mManifestStringFine, -1, mUid))
-                .thenReturn(mFineLocationPermission);
-        when(mMockContext.checkPermission(NETWORK_SETTINGS, -1, mUid))
-                .thenReturn(mNetworkSettingsPermission);
-        when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(mIsLocationEnabled);
-    }
-
-    private Answer<Integer> createPermissionAnswer() {
-        return new Answer<Integer>() {
-            @Override
-            public Integer answer(InvocationOnMock invocation) {
-                int myUid = (int) invocation.getArguments()[1];
-                String myPermission = (String) invocation.getArguments()[0];
-                mPermissionsList.get(myPermission);
-                if (mPermissionsList.containsKey(myPermission)) {
-                    int uid = mPermissionsList.get(myPermission);
-                    if (myUid == uid) {
-                        return PackageManager.PERMISSION_GRANTED;
-                    }
-                }
-                return PackageManager.PERMISSION_DENIED;
-            }
-        };
-    }
-
-    @Test
-    public void testEnforceLocationPermission_HasAllPermissions_BeforeQ() throws Exception {
-        mIsLocationEnabled = true;
-        mThrowSecurityException = false;
-        mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED;
-        mAllowCoarseLocationApps = AppOpsManager.MODE_ALLOWED;
-        mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
-        mUid = mCurrentUser;
-        setupTestCase();
-
-        final int result =
-                mChecker.checkLocationPermissionWithDetailInfo(
-                        TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
-        assertEquals(LocationPermissionChecker.SUCCEEDED, result);
-    }
-
-    @Test
-    public void testEnforceLocationPermission_HasAllPermissions_AfterQ() throws Exception {
-        mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.Q;
-        mIsLocationEnabled = true;
-        mThrowSecurityException = false;
-        mUid = mCurrentUser;
-        mFineLocationPermission = PackageManager.PERMISSION_GRANTED;
-        mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED;
-        mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
-        setupTestCase();
-
-        final int result =
-                mChecker.checkLocationPermissionWithDetailInfo(
-                        TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
-        assertEquals(LocationPermissionChecker.SUCCEEDED, result);
-    }
-
-    @Test
-    public void testEnforceLocationPermission_PkgNameAndUidMismatch() throws Exception {
-        mThrowSecurityException = true;
-        mIsLocationEnabled = true;
-        mFineLocationPermission = PackageManager.PERMISSION_GRANTED;
-        mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED;
-        mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
-        setupTestCase();
-
-        assertThrows(SecurityException.class,
-                () -> mChecker.checkLocationPermissionWithDetailInfo(
-                        TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
-    }
-
-    @Test
-    public void testenforceCanAccessScanResults_NoCoarseLocationPermission() throws Exception {
-        mThrowSecurityException = false;
-        mIsLocationEnabled = true;
-        setupTestCase();
-
-        final int result =
-                mChecker.checkLocationPermissionWithDetailInfo(
-                        TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
-        assertEquals(LocationPermissionChecker.ERROR_LOCATION_PERMISSION_MISSING, result);
-    }
-
-    @Test
-    public void testenforceCanAccessScanResults_NoFineLocationPermission() throws Exception {
-        mThrowSecurityException = false;
-        mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.Q;
-        mIsLocationEnabled = true;
-        mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED;
-        mAllowFineLocationApps = AppOpsManager.MODE_ERRORED;
-        mUid = MANAGED_PROFILE_UID;
-        setupTestCase();
-
-        final int result =
-                mChecker.checkLocationPermissionWithDetailInfo(
-                        TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
-        assertEquals(LocationPermissionChecker.ERROR_LOCATION_PERMISSION_MISSING, result);
-        verify(mMockAppOps, never()).noteOp(anyInt(), anyInt(), anyString());
-    }
-
-    @Test
-    public void testenforceCanAccessScanResults_LocationModeDisabled() throws Exception {
-        mThrowSecurityException = false;
-        mUid = MANAGED_PROFILE_UID;
-        mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
-        mPermissionsList.put(mInteractAcrossUsersFullPermission, mUid);
-        mIsLocationEnabled = false;
-
-        setupTestCase();
-
-        final int result =
-                mChecker.checkLocationPermissionWithDetailInfo(
-                        TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
-        assertEquals(LocationPermissionChecker.ERROR_LOCATION_MODE_OFF, result);
-    }
-
-    @Test
-    public void testenforceCanAccessScanResults_LocationModeDisabledHasNetworkSettings()
-            throws Exception {
-        mThrowSecurityException = false;
-        mIsLocationEnabled = false;
-        mNetworkSettingsPermission = PackageManager.PERMISSION_GRANTED;
-        setupTestCase();
-
-        final int result =
-                mChecker.checkLocationPermissionWithDetailInfo(
-                        TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
-        assertEquals(LocationPermissionChecker.SUCCEEDED, result);
-    }
-
-
-    private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
-        try {
-            r.run();
-            Assert.fail("Expected " + exceptionClass + " to be thrown.");
-        } catch (Exception exception) {
-            assertTrue(exceptionClass.isInstance(exception));
-        }
-    }
-}
diff --git a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
index c01bb75..e41805d 100644
--- a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
+++ b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
@@ -22,7 +22,6 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.os.PersistableBundle;
 import android.os.RemoteException;
@@ -32,6 +31,7 @@
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 
 import java.util.concurrent.Executor;
 
@@ -42,51 +42,23 @@
 @RunWith(AndroidJUnit4.class)
 public class RangingManagerTest {
 
-    private static final IUwbAdapter ADAPTER = mock(IUwbAdapter.class);
     private static final Executor EXECUTOR = UwbTestUtils.getExecutor();
     private static final PersistableBundle PARAMS = new PersistableBundle();
     private static final @RangingChangeReason int REASON = RangingChangeReason.UNKNOWN;
 
     @Test
     public void testOpenSession_OpenRangingInvoked() throws RemoteException {
-        RangingManager rangingManager = new RangingManager(ADAPTER);
+        IUwbAdapter adapter = mock(IUwbAdapter.class);
+        RangingManager rangingManager = new RangingManager(adapter);
         RangingSession.Callback callback = mock(RangingSession.Callback.class);
         rangingManager.openSession(PARAMS, EXECUTOR, callback);
-        verify(ADAPTER, times(1)).openRanging(eq(rangingManager), eq(PARAMS));
-    }
-
-    @Test
-    public void testOpenSession_ErrorIfSameSessionHandleReturned() throws RemoteException {
-        RangingManager rangingManager = new RangingManager(ADAPTER);
-        RangingSession.Callback callback = mock(RangingSession.Callback.class);
-        SessionHandle handle = new SessionHandle(1);
-        when(ADAPTER.openRanging(any(), any())).thenReturn(handle);
-
-        rangingManager.openSession(PARAMS, EXECUTOR, callback);
-
-        // Calling openSession will cause the same session handle to be returned. The onClosed
-        // callback should be invoked
-        RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
-        rangingManager.openSession(PARAMS, EXECUTOR, callback2);
-        verify(callback, times(0)).onClosed(anyInt(), any());
-        verify(callback2, times(1)).onClosed(anyInt(), any());
-    }
-
-    @Test
-    public void testOnRangingOpened_ValidSessionHandle() throws RemoteException {
-        RangingManager rangingManager = new RangingManager(ADAPTER);
-        RangingSession.Callback callback = mock(RangingSession.Callback.class);
-        SessionHandle handle = new SessionHandle(1);
-        when(ADAPTER.openRanging(any(), any())).thenReturn(handle);
-
-        rangingManager.openSession(PARAMS, EXECUTOR, callback);
-        rangingManager.onRangingOpened(handle);
-        verify(callback, times(1)).onOpened(any());
+        verify(adapter, times(1)).openRanging(any(), eq(rangingManager), eq(PARAMS));
     }
 
     @Test
     public void testOnRangingOpened_InvalidSessionHandle() throws RemoteException {
-        RangingManager rangingManager = new RangingManager(ADAPTER);
+        IUwbAdapter adapter = mock(IUwbAdapter.class);
+        RangingManager rangingManager = new RangingManager(adapter);
         RangingSession.Callback callback = mock(RangingSession.Callback.class);
 
         rangingManager.onRangingOpened(new SessionHandle(2));
@@ -95,18 +67,20 @@
 
     @Test
     public void testOnRangingOpened_MultipleSessionsRegistered() throws RemoteException {
-        SessionHandle sessionHandle1 = new SessionHandle(1);
-        SessionHandle sessionHandle2 = new SessionHandle(2);
+        IUwbAdapter adapter = mock(IUwbAdapter.class);
         RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
         RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
+        ArgumentCaptor<SessionHandle> sessionHandleCaptor =
+                ArgumentCaptor.forClass(SessionHandle.class);
 
-        when(ADAPTER.openRanging(any(), any()))
-                .thenReturn(sessionHandle1)
-                .thenReturn(sessionHandle2);
-
-        RangingManager rangingManager = new RangingManager(ADAPTER);
+        RangingManager rangingManager = new RangingManager(adapter);
         rangingManager.openSession(PARAMS, EXECUTOR, callback1);
+        verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();
+
         rangingManager.openSession(PARAMS, EXECUTOR, callback2);
+        verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
 
         rangingManager.onRangingOpened(sessionHandle1);
         verify(callback1, times(1)).onOpened(any());
@@ -119,12 +93,17 @@
 
     @Test
     public void testCorrectCallbackInvoked() throws RemoteException {
-        RangingManager rangingManager = new RangingManager(ADAPTER);
+        IUwbAdapter adapter = mock(IUwbAdapter.class);
+        RangingManager rangingManager = new RangingManager(adapter);
         RangingSession.Callback callback = mock(RangingSession.Callback.class);
-        SessionHandle handle = new SessionHandle(1);
-        when(ADAPTER.openRanging(any(), any())).thenReturn(handle);
+
+        ArgumentCaptor<SessionHandle> sessionHandleCaptor =
+                ArgumentCaptor.forClass(SessionHandle.class);
 
         rangingManager.openSession(PARAMS, EXECUTOR, callback);
+        verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle handle = sessionHandleCaptor.getValue();
+
         rangingManager.onRangingOpened(handle);
         verify(callback, times(1)).onOpened(any());
 
@@ -156,20 +135,23 @@
 
     @Test
     public void testOnRangingClosed_MultipleSessionsRegistered() throws RemoteException {
+        IUwbAdapter adapter = mock(IUwbAdapter.class);
         // Verify that if multiple sessions are registered, only the session that is
         // requested to close receives the associated callbacks
-        SessionHandle sessionHandle1 = new SessionHandle(1);
-        SessionHandle sessionHandle2 = new SessionHandle(2);
         RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
         RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
 
-        when(ADAPTER.openRanging(any(), any()))
-                .thenReturn(sessionHandle1)
-                .thenReturn(sessionHandle2);
+        RangingManager rangingManager = new RangingManager(adapter);
+        ArgumentCaptor<SessionHandle> sessionHandleCaptor =
+                ArgumentCaptor.forClass(SessionHandle.class);
 
-        RangingManager rangingManager = new RangingManager(ADAPTER);
         rangingManager.openSession(PARAMS, EXECUTOR, callback1);
+        verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();
+
         rangingManager.openSession(PARAMS, EXECUTOR, callback2);
+        verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
 
         rangingManager.onRangingClosed(sessionHandle1, REASON, PARAMS);
         verify(callback1, times(1)).onClosed(anyInt(), any());
@@ -182,19 +164,22 @@
 
     @Test
     public void testOnRangingReport_MultipleSessionsRegistered() throws RemoteException {
-        SessionHandle sessionHandle1 = new SessionHandle(1);
-        SessionHandle sessionHandle2 = new SessionHandle(2);
+        IUwbAdapter adapter = mock(IUwbAdapter.class);
         RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
         RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
 
-        when(ADAPTER.openRanging(any(), any()))
-                .thenReturn(sessionHandle1)
-                .thenReturn(sessionHandle2);
+        ArgumentCaptor<SessionHandle> sessionHandleCaptor =
+                ArgumentCaptor.forClass(SessionHandle.class);
 
-        RangingManager rangingManager = new RangingManager(ADAPTER);
+        RangingManager rangingManager = new RangingManager(adapter);
         rangingManager.openSession(PARAMS, EXECUTOR, callback1);
+        verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();
+
         rangingManager.onRangingStarted(sessionHandle1, PARAMS);
         rangingManager.openSession(PARAMS, EXECUTOR, callback2);
+        verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
         rangingManager.onRangingStarted(sessionHandle2, PARAMS);
 
         rangingManager.onRangingResult(sessionHandle1, UwbTestUtils.getRangingReports(1));
@@ -232,17 +217,24 @@
 
     private void runReason(@RangingChangeReason int reasonIn,
             @RangingSession.Callback.Reason int reasonOut) throws RemoteException {
-        RangingManager rangingManager = new RangingManager(ADAPTER);
+        IUwbAdapter adapter = mock(IUwbAdapter.class);
+        RangingManager rangingManager = new RangingManager(adapter);
         RangingSession.Callback callback = mock(RangingSession.Callback.class);
-        SessionHandle handle = new SessionHandle(1);
-        when(ADAPTER.openRanging(any(), any())).thenReturn(handle);
+
+        ArgumentCaptor<SessionHandle> sessionHandleCaptor =
+                ArgumentCaptor.forClass(SessionHandle.class);
+
         rangingManager.openSession(PARAMS, EXECUTOR, callback);
+        verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle handle = sessionHandleCaptor.getValue();
 
         rangingManager.onRangingOpenFailed(handle, reasonIn, PARAMS);
         verify(callback, times(1)).onOpenFailed(eq(reasonOut), eq(PARAMS));
 
         // Open a new session
         rangingManager.openSession(PARAMS, EXECUTOR, callback);
+        verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        handle = sessionHandleCaptor.getValue();
         rangingManager.onRangingOpened(handle);
 
         rangingManager.onRangingStartFailed(handle, reasonIn, PARAMS);
diff --git a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
index 8e7f7c56..75c6924 100644
--- a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
+++ b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
@@ -16,34 +16,23 @@
 
 package android.uwb;
 
-import android.content.Context;
-import android.content.pm.PackageManager;
 import android.os.SystemClock;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.concurrent.Executor;
 
 public class UwbTestUtils {
     private UwbTestUtils() {}
 
-    public static boolean isUwbSupported(Context context) {
-        PackageManager packageManager = context.getPackageManager();
-        return packageManager.hasSystemFeature(PackageManager.FEATURE_UWB);
-    }
-
     public static AngleMeasurement getAngleMeasurement() {
-        return new AngleMeasurement.Builder()
-                .setRadians(getDoubleInRange(-Math.PI, Math.PI))
-                .setErrorRadians(getDoubleInRange(0, Math.PI))
-                .setConfidenceLevel(getDoubleInRange(0, 1))
-                .build();
+        return new AngleMeasurement(
+                getDoubleInRange(-Math.PI, Math.PI),
+                getDoubleInRange(0, Math.PI),
+                getDoubleInRange(0, 1));
     }
 
     public static AngleOfArrivalMeasurement getAngleOfArrivalMeasurement() {
-        return new AngleOfArrivalMeasurement.Builder()
+        return new AngleOfArrivalMeasurement.Builder(getAngleMeasurement())
                 .setAltitude(getAngleMeasurement())
-                .setAzimuth(getAngleMeasurement())
                 .build();
     }
 
@@ -69,14 +58,6 @@
                 .build();
     }
 
-    public static List<RangingMeasurement> getRangingMeasurements(int num) {
-        List<RangingMeasurement> result = new ArrayList<>();
-        for (int i = 0; i < num; i++) {
-            result.add(getRangingMeasurement());
-        }
-        return result;
-    }
-
     public static RangingReport getRangingReports(int numMeasurements) {
         RangingReport.Builder builder = new RangingReport.Builder();
         for (int i = 0; i < numMeasurements; i++) {
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 354d83c..653e690 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -451,6 +451,7 @@
         <permission name="android.permission.MANAGE_SOUND_TRIGGER" />
         <permission name="android.permission.CAPTURE_AUDIO_HOTWORD" />
         <permission name="android.permission.MODIFY_QUIET_MODE" />
+        <permission name="android.permission.MANAGE_APP_HIBERNATION"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index fe5c1be..d048efc 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -1196,13 +1196,11 @@
 
     /** @hide */
     public boolean isSupportedAxes(int axis) {
-        if (mSupportedAxes == null) {
-            synchronized (this) {
+        synchronized (this) {
+            if (mSupportedAxes == null) {
+                mSupportedAxes = nativeGetSupportedAxes(native_instance);
                 if (mSupportedAxes == null) {
-                    mSupportedAxes = nativeGetSupportedAxes(native_instance);
-                    if (mSupportedAxes == null) {
-                        mSupportedAxes = EMPTY_AXES;
-                    }
+                    mSupportedAxes = EMPTY_AXES;
                 }
             }
         }
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index ed789f0..72cea0c 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
-import android.security.usermanager.IKeystoreUserManager;
+import android.security.maintenance.IKeystoreMaintenance;
 import android.system.keystore2.Domain;
 import android.system.keystore2.ResponseCode;
 import android.util.Log;
@@ -34,9 +34,9 @@
 
     public static final int SYSTEM_ERROR = ResponseCode.SYSTEM_ERROR;
 
-    private static IKeystoreUserManager getService() {
-        return IKeystoreUserManager.Stub.asInterface(
-                ServiceManager.checkService("android.security.usermanager"));
+    private static IKeystoreMaintenance getService() {
+        return IKeystoreMaintenance.Stub.asInterface(
+                ServiceManager.checkService("android.security.maintenance"));
     }
 
     /**
@@ -121,4 +121,36 @@
             return SYSTEM_ERROR;
         }
     }
+
+    /**
+     * Queries user state from Keystore 2.0.
+     *
+     * @param userId - Android user id of the user.
+     * @return UserState enum variant as integer if successful or an error
+     */
+    public static int getState(int userId) {
+        try {
+            return getService().getState(userId);
+        } catch (ServiceSpecificException e) {
+            Log.e(TAG, "getState failed", e);
+            return e.errorCode;
+        } catch (Exception e) {
+            Log.e(TAG, "Can not connect to keystore", e);
+            return SYSTEM_ERROR;
+        }
+    }
+
+    /**
+     * Informs Keystore 2.0 that an off body event was detected.
+     */
+    public static void onDeviceOffBody() {
+        if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return;
+        try {
+            getService().onDeviceOffBody();
+        } catch (Exception e) {
+            // TODO This fails open. This is not a regression with respect to keystore1 but it
+            //      should get fixed.
+            Log.e(TAG, "Error while reporting device off body event.", e);
+        }
+    }
 }
diff --git a/keystore/java/android/security/GenerateRkpKey.java b/keystore/java/android/security/GenerateRkpKey.java
new file mode 100644
index 0000000..a1a7aa8
--- /dev/null
+++ b/keystore/java/android/security/GenerateRkpKey.java
@@ -0,0 +1,99 @@
+/*
+ * 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;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+/**
+ * GenerateKey is a helper class to handle interactions between Keystore and the RemoteProvisioner
+ * app. There are two cases where Keystore should use this class.
+ *
+ * (1) : An app generates a new attested key pair, so Keystore calls notifyKeyGenerated to let the
+ *       RemoteProvisioner app check if the state of the attestation key pool is getting low enough
+ *       to warrant provisioning more attestation certificates early.
+ *
+ * (2) : An app attempts to generate a new key pair, but the keystore service discovers it is out of
+ *       attestation key pairs and cannot provide one for the given application. Keystore can then
+ *       make a blocking call on notifyEmpty to allow the RemoteProvisioner app to get another
+ *       attestation certificate chain provisioned.
+ *
+ * In most cases, the proper usage of (1) should preclude the need for (2).
+ *
+ * @hide
+ */
+public class GenerateRkpKey {
+
+    private IGenerateRkpKeyService mBinder;
+    private Context mContext;
+
+    private ServiceConnection mConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            mBinder = IGenerateRkpKeyService.Stub.asInterface(service);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName className) {
+            mBinder = null;
+        }
+    };
+
+    /**
+     * Constructor which takes a Context object.
+     */
+    public GenerateRkpKey(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Fulfills the use case of (2) described in the class documentation. Blocks until the
+     * RemoteProvisioner application can get new attestation keys signed by the server.
+     */
+    public void notifyEmpty(int securityLevel) throws RemoteException {
+        Intent intent = new Intent(IGenerateRkpKeyService.class.getName());
+        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+        intent.setComponent(comp);
+        if (comp == null || !mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
+            throw new RemoteException("Failed to bind to GenerateKeyService");
+        }
+        if (mBinder != null) {
+            mBinder.generateKey(securityLevel);
+        }
+        mContext.unbindService(mConnection);
+    }
+
+    /**
+     * FUlfills the use case of (1) described in the class documentation. Non blocking call.
+     */
+    public void notifyKeyGenerated(int securityLevel) throws RemoteException {
+        Intent intent = new Intent(IGenerateRkpKeyService.class.getName());
+        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+        intent.setComponent(comp);
+        if (comp == null || !mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
+            throw new RemoteException("Failed to bind to GenerateKeyService");
+        }
+        if (mBinder != null) {
+            mBinder.notifyKeyGenerated(securityLevel);
+        }
+        mContext.unbindService(mConnection);
+    }
+}
diff --git a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl b/keystore/java/android/security/GenerateRkpKeyException.java
similarity index 61%
copy from packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
copy to keystore/java/android/security/GenerateRkpKeyException.java
index 7979afc..a2d65e4 100644
--- a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
+++ b/keystore/java/android/security/GenerateRkpKeyException.java
@@ -1,12 +1,11 @@
-/**
- *
+/*
  * 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
+ *      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,
@@ -15,9 +14,18 @@
  * limitations under the License.
  */
 
-package android.net;
+package android.security;
 
-/** @hide */
-oneway interface IOnSetOemNetworkPreferenceListener {
-    void onComplete();
+/**
+ * Thrown on problems in attempting to attest to a key using a remotely provisioned key.
+ *
+ * @hide
+ */
+public class GenerateRkpKeyException extends Exception {
+
+    /**
+     * Constructs a new {@code GenerateRkpKeyException}.
+     */
+    public GenerateRkpKeyException() {
+    }
 }
diff --git a/keystore/java/android/security/IGenerateRkpKeyService.aidl b/keystore/java/android/security/IGenerateRkpKeyService.aidl
new file mode 100644
index 0000000..5f1d669
--- /dev/null
+++ b/keystore/java/android/security/IGenerateRkpKeyService.aidl
@@ -0,0 +1,36 @@
+/**
+ * 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;
+
+/**
+ * Interface to allow the framework to notify the RemoteProvisioner app when keys are empty. This
+ * will be used if Keystore replies with an error code NO_KEYS_AVAILABLE in response to an
+ * attestation request. The framework can then synchronously call generateKey() to get more
+ * attestation keys generated and signed. Upon return, the caller can be certain an attestation key
+ * is available.
+ *
+ * @hide
+ */
+interface IGenerateRkpKeyService {
+    /**
+     * Ping the provisioner service to let it know an app generated a key. This may or may not have
+     * consumed a remotely provisioned attestation key, so the RemoteProvisioner app should check.
+     */
+    oneway void notifyKeyGenerated(in int securityLevel);
+    /** Ping the provisioner service to indicate there are no remaining attestation keys left. */
+    void generateKey(in int securityLevel);
+}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 93658e6..b05149e 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -43,6 +43,7 @@
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeystoreResponse;
 import android.security.keystore.UserNotAuthenticatedException;
+import android.security.maintenance.UserState;
 import android.system.keystore2.Domain;
 import android.util.Log;
 
@@ -196,6 +197,19 @@
     public State state(int userId) {
         final int ret;
         try {
+            if (android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) {
+                int userState = AndroidKeyStoreMaintenance.getState(userId);
+                switch (userState) {
+                    case UserState.UNINITIALIZED:
+                        return KeyStore.State.UNINITIALIZED;
+                    case UserState.LSKF_UNLOCKED:
+                        return KeyStore.State.UNLOCKED;
+                    case UserState.LSKF_LOCKED:
+                        return KeyStore.State.LOCKED;
+                    default:
+                        throw new AssertionError(userState);
+                }
+            }
             ret = mBinder.getState(userId);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
@@ -1190,6 +1204,7 @@
      * Notify keystore that the device went off-body.
      */
     public void onDeviceOffBody() {
+        AndroidKeyStoreMaintenance.onDeviceOffBody();
         try {
             mBinder.onDeviceOffBody();
         } catch (RemoteException e) {
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index 6ac3821..75e248e 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -18,8 +18,7 @@
 
 import android.annotation.NonNull;
 import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
-import android.os.Build;
+import android.compat.annotation.Disabled;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
@@ -86,7 +85,7 @@
      * successfully conclude an operation.
      */
     @ChangeId
-    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+    @Disabled // See b/180133780
     static final long KEYSTORE_OPERATION_CREATION_MAY_FAIL = 169897160L;
 
     // Never use mBinder directly, use KeyStore2.getService() instead or better yet
diff --git a/keystore/java/android/security/keystore/AttestationUtils.java b/keystore/java/android/security/keystore/AttestationUtils.java
index f1eea82..1eb8541 100644
--- a/keystore/java/android/security/keystore/AttestationUtils.java
+++ b/keystore/java/android/security/keystore/AttestationUtils.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.Build;
-import android.security.KeyStore;
 import android.security.keymaster.KeymasterArguments;
 import android.security.keymaster.KeymasterCertificateChain;
 import android.security.keymaster.KeymasterDefs;
@@ -34,9 +33,14 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.nio.charset.StandardCharsets;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.SecureRandom;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.security.spec.ECGenParameterSpec;
 import java.util.Collection;
+import java.util.Random;
 import java.util.Set;
 
 /**
@@ -256,24 +260,51 @@
     @NonNull public static X509Certificate[] attestDeviceIds(Context context,
             @NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws
             DeviceIdAttestationException {
-        final KeymasterArguments attestArgs = prepareAttestationArgumentsForDeviceId(
-                context, idTypes, attestationChallenge);
+        String keystoreAlias = generateRandomAlias();
+        KeyGenParameterSpec.Builder builder =
+                new KeyGenParameterSpec.Builder(keystoreAlias, KeyProperties.PURPOSE_SIGN)
+                        .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
+                        .setDigests(KeyProperties.DIGEST_SHA256)
+                        .setAttestationChallenge(attestationChallenge);
 
-        // Perform attestation.
-        final KeymasterCertificateChain outChain = new KeymasterCertificateChain();
-        final int errorCode = KeyStore.getInstance().attestDeviceIds(attestArgs, outChain);
-        if (errorCode != KeyStore.NO_ERROR) {
-            throw new DeviceIdAttestationException("Unable to perform attestation",
-                    KeyStore.getKeyStoreException(errorCode));
+        if (idTypes != null) {
+            builder.setAttestationIds(idTypes);
+            builder.setDevicePropertiesAttestationIncluded(true);
         }
 
         try {
-            return parseCertificateChain(outChain);
-        } catch (KeyAttestationException e) {
-            throw new DeviceIdAttestationException(e.getMessage(), e);
+            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
+                    KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
+            keyPairGenerator.initialize(builder.build());
+            keyPairGenerator.generateKeyPair();
+
+            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+            keyStore.load(null);
+
+            X509Certificate[] certificateChain =
+                    (X509Certificate[]) keyStore.getCertificateChain(keystoreAlias);
+
+            keyStore.deleteEntry(keystoreAlias);
+
+            return certificateChain;
+        } catch (SecurityException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new DeviceIdAttestationException("Unable to perform attestation", e);
         }
     }
 
+    private static String generateRandomAlias() {
+        Random random = new SecureRandom();
+        StringBuilder builder = new StringBuilder();
+        // Pick random uppercase letters, A-Z.  20 of them gives us ~94 bits of entropy, which
+        // should prevent any conflicts with app-selected aliases, even for very unlucky users.
+        for (int i = 0; i < 20; ++i) {
+            builder.append(random.nextInt(26) + 'A');
+        }
+        return builder.toString();
+    }
+
     /**
      * Returns true if the attestation chain provided is a valid key attestation chain.
      * @hide
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 72735a7..9ca551b 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -288,7 +288,7 @@
     private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048
 
     private final String mKeystoreAlias;
-    private final int mNamespace;
+    private final @KeyProperties.Namespace int mNamespace;
     private final int mKeySize;
     private final AlgorithmParameterSpec mSpec;
     private final X500Principal mCertificateSubject;
@@ -331,7 +331,7 @@
      */
     public KeyGenParameterSpec(
             String keyStoreAlias,
-            int namespace,
+            @KeyProperties.Namespace int namespace,
             int keySize,
             AlgorithmParameterSpec spec,
             X500Principal certificateSubject,
@@ -467,12 +467,12 @@
      *
      * @return The numeric namespace as configured in the keystore2_key_contexts files of Android's
      *         SEPolicy.
-     *         TODO b/171806779 link to public Keystore 2.0 documentation.
-     *              See bug for more details for now.
+     *         See <a href="https://source.android.com/security/keystore#access-control">
+     *             Keystore 2.0 access control</a>
      * @hide
      */
     @SystemApi
-    public int getNamespace() {
+    public @KeyProperties.Namespace int getNamespace() {
         return mNamespace;
     }
 
@@ -896,7 +896,7 @@
         private final String mKeystoreAlias;
         private @KeyProperties.PurposeEnum int mPurposes;
 
-        private int mNamespace = KeyProperties.NAMESPACE_APPLICATION;
+        private @KeyProperties.Namespace int mNamespace = KeyProperties.NAMESPACE_APPLICATION;
         private int mKeySize = -1;
         private AlgorithmParameterSpec mSpec;
         private X500Principal mCertificateSubject;
@@ -1042,16 +1042,16 @@
          * keys between system and vendor components, e.g., WIFI settings and WPA supplicant.
          *
          * @param namespace Numeric SELinux namespace as configured in keystore2_key_contexts
-         *                  of Android's SEPolicy.
-         *                  TODO b/171806779 link to public Keystore 2.0 documentation.
-         *                       See bug for more details for now.
+         *         of Android's SEPolicy.
+         *         See <a href="https://source.android.com/security/keystore#access-control">
+         *             Keystore 2.0 access control</a>
          * @return this Builder object.
          *
          * @hide
          */
         @SystemApi
         @NonNull
-        public Builder setNamespace(int namespace) {
+        public Builder setNamespace(@KeyProperties.Namespace int namespace) {
             mNamespace = namespace;
             return this;
         }
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index 7b0fa91..682d12a 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -892,6 +892,22 @@
     }
 
     /**
+     * Namespaces provide system developers and vendors with a way to use keystore without
+     * requiring an applications uid. Namespaces can be configured using SEPolicy.
+     * See <a href="https://source.android.com/security/keystore#access-control">
+     *     Keystore 2.0 access-control</a>
+     * {@See KeyGenParameterSpec.Builder#setNamespace}
+     * {@See android.security.keystore2.AndroidKeyStoreLoadStoreParameter}
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "NAMESPACE_" }, value = {
+            NAMESPACE_APPLICATION,
+            NAMESPACE_WIFI,
+    })
+    public @interface Namespace {}
+
+    /**
      * This value indicates the implicit keystore namespace of the calling application.
      * It is used by default. Only select system components can choose a different namespace
      * which it must be configured in SEPolicy.
@@ -912,14 +928,12 @@
      * For legacy support, translate namespaces into known UIDs.
      * @hide
      */
-    public static int namespaceToLegacyUid(int namespace) {
+    public static int namespaceToLegacyUid(@Namespace int namespace) {
         switch (namespace) {
             case NAMESPACE_APPLICATION:
                 return KeyStore.UID_SELF;
             case NAMESPACE_WIFI:
                 return Process.WIFI_UID;
-            // TODO Translate WIFI and VPN UIDs once the namespaces are defined.
-            //  b/171305388 and b/171305607
             default:
                 throw new IllegalArgumentException("No UID corresponding to namespace "
                         + namespace);
@@ -930,14 +944,12 @@
      * For legacy support, translate namespaces into known UIDs.
      * @hide
      */
-    public static int legacyUidToNamespace(int uid) {
+    public static @Namespace int legacyUidToNamespace(int uid) {
         switch (uid) {
             case KeyStore.UID_SELF:
                 return NAMESPACE_APPLICATION;
             case Process.WIFI_UID:
                 return NAMESPACE_WIFI;
-            // TODO Translate WIFI and VPN UIDs once the namespaces are defined.
-            //  b/171305388 and b/171305607
             default:
                 throw new IllegalArgumentException("No namespace corresponding to uid "
                         + uid);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
index 2ee952c..9d8a5ef 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
@@ -123,8 +123,9 @@
             throws InvalidKeyException {
         resetAll();
 
-        if (!(key instanceof AndroidKeyStorePrivateKey
-                || key instanceof AndroidKeyStoreSecretKey)) {
+        // Public key operations get diverted to the default provider.
+        if (!(key instanceof AndroidKeyStorePrivateKey)
+                && (key instanceof PrivateKey || key instanceof PublicKey)) {
             try {
                 mCipher = Cipher.getInstance(getTransform());
                 String transform = getTransform();
@@ -184,8 +185,9 @@
             SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
         resetAll();
 
-        if (!(key instanceof AndroidKeyStorePrivateKey
-                || key instanceof AndroidKeyStoreSecretKey)) {
+        // Public key operations get diverted to the default provider.
+        if (!(key instanceof AndroidKeyStorePrivateKey)
+                && (key instanceof PrivateKey || key instanceof PublicKey)) {
             try {
                 mCipher = Cipher.getInstance(getTransform());
                 mCipher.init(opmode, key, params, random);
@@ -213,8 +215,9 @@
             SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
         resetAll();
 
-        if (!(key instanceof AndroidKeyStorePrivateKey
-                || key instanceof AndroidKeyStoreSecretKey)) {
+        // Public key operations get diverted to the default provider.
+        if (!(key instanceof AndroidKeyStorePrivateKey)
+                && (key instanceof PrivateKey || key instanceof PublicKey)) {
             try {
                 mCipher = Cipher.getInstance(getTransform());
                 mCipher.init(opmode, key, params, random);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index e401add..2d8901a 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -24,6 +24,9 @@
 import android.hardware.security.keymint.SecurityLevel;
 import android.hardware.security.keymint.Tag;
 import android.os.Build;
+import android.os.RemoteException;
+import android.security.GenerateRkpKey;
+import android.security.GenerateRkpKeyException;
 import android.security.KeyPairGeneratorSpec;
 import android.security.KeyStore;
 import android.security.KeyStore2;
@@ -520,6 +523,18 @@
 
     @Override
     public KeyPair generateKeyPair() {
+        try {
+            return generateKeyPairHelper();
+        } catch (GenerateRkpKeyException e) {
+            try {
+                return generateKeyPairHelper();
+            } catch (GenerateRkpKeyException f) {
+                throw new ProviderException("Failed to provision new attestation keys.");
+            }
+        }
+    }
+
+    private KeyPair generateKeyPairHelper() throws GenerateRkpKeyException {
         if (mKeyStore == null || mSpec == null) {
             throw new IllegalStateException("Not initialized");
         }
@@ -557,13 +572,30 @@
             AndroidKeyStorePublicKey publicKey =
                     AndroidKeyStoreProvider.makeAndroidKeyStorePublicKeyFromKeyEntryResponse(
                             descriptor, metadata, iSecurityLevel, mKeymasterAlgorithm);
-
+            GenerateRkpKey keyGen = new GenerateRkpKey(KeyStore.getApplicationContext());
+            try {
+                if (mSpec.getAttestationChallenge() != null) {
+                    keyGen.notifyKeyGenerated(securityLevel);
+                }
+            } catch (RemoteException e) {
+                // This is not really an error state, and necessarily does not apply to non RKP
+                // systems or hybrid systems where RKP is not currently turned on.
+                Log.d(TAG, "Couldn't connect to the RemoteProvisioner backend.");
+            }
             success = true;
             return new KeyPair(publicKey, publicKey.getPrivateKey());
         } catch (android.security.KeyStoreException e) {
             switch(e.getErrorCode()) {
                 case KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE:
                     throw new StrongBoxUnavailableException("Failed to generated key pair.", e);
+                case ResponseCode.OUT_OF_KEYS:
+                    GenerateRkpKey keyGen = new GenerateRkpKey(KeyStore.getApplicationContext());
+                    try {
+                        keyGen.notifyEmpty(securityLevel);
+                    } catch (RemoteException f) {
+                        throw new ProviderException("Failed to talk to RemoteProvisioner", f);
+                    }
+                    throw new GenerateRkpKeyException();
                 default:
                     ProviderException p = new ProviderException("Failed to generate key pair.", e);
                     if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java b/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java
index 0c6744f..25b1c86 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java
@@ -16,6 +16,8 @@
 
 package android.security.keystore2;
 
+import android.security.keystore.KeyProperties;
+
 import java.security.KeyStore;
 import java.security.KeyStore.ProtectionParameter;
 
@@ -24,9 +26,9 @@
  */
 public class AndroidKeyStoreLoadStoreParameter implements KeyStore.LoadStoreParameter {
 
-    private final int mNamespace;
+    private final @KeyProperties.Namespace int mNamespace;
 
-    public AndroidKeyStoreLoadStoreParameter(int namespace) {
+    public AndroidKeyStoreLoadStoreParameter(@KeyProperties.Namespace int namespace) {
         mNamespace = namespace;
     }
 
@@ -35,7 +37,7 @@
         return null;
     }
 
-    int getNamespace() {
+    @KeyProperties.Namespace int getNamespace() {
         return mNamespace;
     }
 }
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index d36695b..ba6d22f 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -145,23 +145,15 @@
         sInstalled = true;
 
         Security.addProvider(new AndroidKeyStoreProvider());
-        Security.addProvider(
-                new android.security.keystore.AndroidKeyStoreProvider(
-                        "AndroidKeyStoreLegacy"));
         Provider workaroundProvider = new AndroidKeyStoreBCWorkaroundProvider();
-        Provider legacyWorkaroundProvider =
-                new android.security.keystore.AndroidKeyStoreBCWorkaroundProvider(
-                        "AndroidKeyStoreBCWorkaroundLegacy");
         if (bcProviderIndex != -1) {
             // Bouncy Castle provider found -- install the workaround provider above it.
             // insertProviderAt uses 1-based positions.
-            Security.insertProviderAt(legacyWorkaroundProvider, bcProviderIndex + 1);
             Security.insertProviderAt(workaroundProvider, bcProviderIndex + 1);
         } else {
             // Bouncy Castle provider not found -- install the workaround provider at lowest
             // priority.
             Security.addProvider(workaroundProvider);
-            Security.addProvider(legacyWorkaroundProvider);
         }
     }
 
@@ -340,11 +332,11 @@
      * @param keyStore The keystore2 backend.
      * @param alias The alias of the key in the Keystore database.
      * @param namespace The a Keystore namespace. This is used by system api only to request
-     *                  Android system specific keystore namespace, which can be configured
-     *                  in the device's SEPolicy. Third party apps and most system components
-     *                  set this parameter to -1 to indicate their application specific namespace.
-     *                  TODO b/171806779 link to public Keystore 2.0 documentation.
-     *                       See bug for more details for now.
+     *         Android system specific keystore namespace, which can be configured
+     *         in the device's SEPolicy. Third party apps and most system components
+     *         set this parameter to -1 to indicate their application specific namespace.
+     *         See <a href="https://source.android.com/security/keystore#access-control">
+     *             Keystore 2.0 access control</a>
      * @hide
      **/
     @NonNull
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 39607ae..32f98a2 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -100,7 +100,7 @@
     public static final String NAME = "AndroidKeyStore";
 
     private KeyStore2 mKeyStore;
-    private int mNamespace = KeyProperties.NAMESPACE_APPLICATION;
+    private @KeyProperties.Namespace int mNamespace = KeyProperties.NAMESPACE_APPLICATION;
 
     @Override
     public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
@@ -1125,7 +1125,7 @@
     @Override
     public void engineLoad(LoadStoreParameter param) throws IOException,
             NoSuchAlgorithmException, CertificateException {
-        int namespace = KeyProperties.NAMESPACE_APPLICATION;
+        @KeyProperties.Namespace int namespace = KeyProperties.NAMESPACE_APPLICATION;
         if (param != null) {
             if (param instanceof AndroidKeyStoreLoadStoreParameter) {
                 namespace = ((AndroidKeyStoreLoadStoreParameter) param).getNamespace();
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 446e81e..c6ab119 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -124,7 +124,7 @@
     SkAndroidFrameworkTraceUtil::setEnableTracing(
             base::GetBoolProperty(PROPERTY_SKIA_ATRACE_ENABLED, false));
 
-    runningInEmulator = base::GetBoolProperty(PROPERTY_QEMU_KERNEL, false);
+    runningInEmulator = base::GetBoolProperty(PROPERTY_IS_EMULATOR, false);
 
     defaultRenderAhead = std::max(-1, std::min(2, base::GetIntProperty(PROPERTY_RENDERAHEAD,
             render_ahead().value_or(0))));
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index d3ecb54d..6ea208e 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -160,7 +160,7 @@
 /**
  * Property for whether this is running in the emulator.
  */
-#define PROPERTY_QEMU_KERNEL "ro.kernel.qemu"
+#define PROPERTY_IS_EMULATOR "ro.boot.qemu"
 
 #define PROPERTY_RENDERAHEAD "debug.hwui.render_ahead"
 
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 1b05c3b..b265ebf 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -2077,6 +2077,65 @@
     }
 
     /**
+     * Sets the streaming start threshold for an <code>AudioTrack</code>.
+     * <p> The streaming start threshold is the buffer level that the written audio
+     * data must reach for audio streaming to start after {@link #play()} is called.
+     * <p> For compressed streams, the size of a frame is considered to be exactly one byte.
+     *
+     * @param startThresholdInFrames the desired start threshold.
+     * @return the actual start threshold in frames value. This is
+     *         an integer between 1 to the buffer capacity
+     *         (see {@link #getBufferCapacityInFrames()}),
+     *         and might change if the output sink changes after track creation.
+     * @throws IllegalStateException if the track is not initialized or the
+     *         track transfer mode is not {@link #MODE_STREAM}.
+     * @throws IllegalArgumentException if startThresholdInFrames is not positive.
+     * @see #getStartThresholdInFrames()
+     */
+    public @IntRange(from = 1) int setStartThresholdInFrames(
+            @IntRange (from = 1) int startThresholdInFrames) {
+        if (mState != STATE_INITIALIZED) {
+            throw new IllegalStateException("AudioTrack is not initialized");
+        }
+        if (mDataLoadMode != MODE_STREAM) {
+            throw new IllegalStateException("AudioTrack must be a streaming track");
+        }
+        if (startThresholdInFrames < 1) {
+            throw new IllegalArgumentException("startThresholdInFrames "
+                    + startThresholdInFrames + " must be positive");
+        }
+        return native_setStartThresholdInFrames(startThresholdInFrames);
+    }
+
+    /**
+     * Returns the streaming start threshold of the <code>AudioTrack</code>.
+     * <p> The streaming start threshold is the buffer level that the written audio
+     * data must reach for audio streaming to start after {@link #play()} is called.
+     * When an <code>AudioTrack</code> is created, the streaming start threshold
+     * is the buffer capacity in frames. If the buffer size in frames is reduced
+     * by {@link #setBufferSizeInFrames(int)} to a value smaller than the start threshold
+     * then that value will be used instead for the streaming start threshold.
+     * <p> For compressed streams, the size of a frame is considered to be exactly one byte.
+     *
+     * @return the current start threshold in frames value. This is
+     *         an integer between 1 to the buffer capacity
+     *         (see {@link #getBufferCapacityInFrames()}),
+     *         and might change if the  output sink changes after track creation.
+     * @throws IllegalStateException if the track is not initialized or the
+     *         track is not {@link #MODE_STREAM}.
+     * @see #setStartThresholdInFrames(int)
+     */
+    public @IntRange (from = 1) int getStartThresholdInFrames() {
+        if (mState != STATE_INITIALIZED) {
+            throw new IllegalStateException("AudioTrack is not initialized");
+        }
+        if (mDataLoadMode != MODE_STREAM) {
+            throw new IllegalStateException("AudioTrack must be a streaming track");
+        }
+        return native_getStartThresholdInFrames();
+    }
+
+    /**
      *  Returns the frame count of the native <code>AudioTrack</code> buffer.
      *  @return current size in frames of the <code>AudioTrack</code> buffer.
      *  @throws IllegalStateException
@@ -4179,6 +4238,8 @@
     private native int native_get_audio_description_mix_level_db(float[] level);
     private native int native_set_dual_mono_mode(int dualMonoMode);
     private native int native_get_dual_mono_mode(int[] dualMonoMode);
+    private native int native_setStartThresholdInFrames(int startThresholdInFrames);
+    private native int native_getStartThresholdInFrames();
 
     //---------------------------------------------------------
     // Utility methods
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 0780c68..7062f83 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -45,6 +45,7 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -4554,6 +4555,128 @@
 
     private native void native_enableOnFrameRenderedListener(boolean enable);
 
+    /**
+     * Returns a list of vendor parameter names.
+     * <p>
+     * This method can be called in any codec state except for released state.
+     *
+     * @return a list containing supported vendor parameters; an empty
+     *         list if no vendor parameters are supported. The order of the
+     *         parameters is arbitrary.
+     * @throws IllegalStateException if in the Released state.
+     */
+    @NonNull
+    public List<String> getSupportedVendorParameters() {
+        return native_getSupportedVendorParameters();
+    }
+
+    @NonNull
+    private native List<String> native_getSupportedVendorParameters();
+
+    /**
+     * Contains description of a parameter.
+     */
+    public static class ParameterDescriptor {
+        private ParameterDescriptor() {}
+
+        /**
+         * Returns the name of the parameter.
+         */
+        @NonNull
+        public String getName() {
+            return mName;
+        }
+
+        /**
+         * Returns the type of the parameter.
+         * {@link MediaFormat#TYPE_NULL} is never returned.
+         */
+        @MediaFormat.Type
+        public int getType() {
+            return mType;
+        }
+
+        private String mName;
+        private @MediaFormat.Type int mType;
+    }
+
+    /**
+     * Describe a parameter with the name.
+     * <p>
+     * This method can be called in any codec state except for released state.
+     *
+     * @param name name of the parameter to describe, typically one from
+     *             {@link #getSupportedVendorParameters}.
+     * @return {@link ParameterDescriptor} object that describes the parameter.
+     *         {@code null} if unrecognized / not able to describe.
+     * @throws IllegalStateException if in the Released state.
+     */
+    @Nullable
+    public ParameterDescriptor getParameterDescriptor(@NonNull String name) {
+        return native_getParameterDescriptor(name);
+    }
+
+    @Nullable
+    private native ParameterDescriptor native_getParameterDescriptor(@NonNull String name);
+
+    /**
+     * Subscribe to vendor parameters, so that changes to these parameters generate
+     * output format change event.
+     * <p>
+     * Unrecognized parameter names or standard (non-vendor) parameter names will be ignored.
+     * {@link #reset} also resets the list of subscribed parameters.
+     * If a parameter in {@code names} is already subscribed, it will remain subscribed.
+     * <p>
+     * This method can be called in any codec state except for released state. When called in
+     * running state with newly subscribed parameters, it takes effect no later than the
+     * processing of the subsequently queued buffer. For the new parameters, the codec will generate
+     * output format change event.
+     * <p>
+     * Note that any vendor parameters set in a {@link #configure} or
+     * {@link #setParameters} call are automatically subscribed.
+     * <p>
+     * See also {@link #INFO_OUTPUT_FORMAT_CHANGED} or {@link Callback#onOutputFormatChanged}
+     * for output format change events.
+     *
+     * @param names names of the vendor parameters to subscribe. This may be an empty list,
+     *              and in that case this method will not change the list of subscribed parameters.
+     * @throws IllegalStateException if in the Released state.
+     */
+    public void subscribeToVendorParameters(@NonNull List<String> names) {
+        native_subscribeToVendorParameters(names);
+    }
+
+    private native void native_subscribeToVendorParameters(@NonNull List<String> names);
+
+    /**
+     * Unsubscribe from vendor parameters, so that changes to these parameters
+     * no longer generate output format change event.
+     * <p>
+     * Unrecognized parameter names, standard (non-vendor) parameter names will be ignored.
+     * {@link #reset} also resets the list of subscribed parameters.
+     * If a parameter in {@code names} is already unsubscribed, it will remain unsubscribed.
+     * <p>
+     * This method can be called in any codec state except for released state. When called in
+     * running state with newly unsubscribed parameters, it takes effect no later than the
+     * processing of the subsequently queued buffer.
+     * <p>
+     * Note that any vendor parameters set in a {@link #configure} or
+     * {@link #setParameters} call are automatically subscribed, and with this method
+     * they can be unsubscribed.
+     * <p>
+     * See also {@link #INFO_OUTPUT_FORMAT_CHANGED} or {@link Callback#onOutputFormatChanged}
+     * for output format change events.
+     *
+     * @param names names of the vendor parameters to unsubscribe. This may be an empty list,
+     *              and in that case this method will not change the list of subscribed parameters.
+     * @throws IllegalStateException if in the Released state.
+     */
+    public void unsubscribeFromVendorParameters(@NonNull List<String> names) {
+        native_unsubscribeFromVendorParameters(names);
+    }
+
+    private native void native_unsubscribeFromVendorParameters(@NonNull List<String> names);
+
     private EventHandler getEventHandlerOn(
             @Nullable Handler handler, @NonNull EventHandler lastHandler) {
         if (handler == null) {
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index f65dfdd..517e192 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -19,6 +19,7 @@
     name: "libmedia_jni",
 
     defaults: ["libcodec2-internal-defaults"],
+    min_sdk_version: "",
 
     srcs: [
         "android_media_ImageWriter.cpp",
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 0b0e162..b3eb8ba 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -53,6 +53,7 @@
 
 #include <media/MediaCodecBuffer.h>
 #include <media/hardware/VideoAPI.h>
+#include <media/stagefright/CodecBase.h>
 #include <media/stagefright/MediaCodec.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -82,6 +83,16 @@
     EVENT_FRAME_RENDERED = 3,
 };
 
+// From MediaFormat.java
+enum {
+    TYPE_NULL           = 0,
+    TYPE_INTEGER        = 1,
+    TYPE_LONG           = 2,
+    TYPE_FLOAT          = 3,
+    TYPE_STRING         = 4,
+    TYPE_BYTE_BUFFER    = 5,
+};
+
 static struct CryptoErrorCodes {
     jint cryptoErrorNoKey;
     jint cryptoErrorKeyExpired;
@@ -138,6 +149,8 @@
 } gByteBufferInfo;
 
 static struct {
+    jclass clazz;
+    jmethodID ctorId;
     jmethodID sizeId;
     jmethodID getId;
     jmethodID addId;
@@ -152,6 +165,13 @@
     jfieldID lockId;
 } gLinearBlockInfo;
 
+static struct {
+    jclass clazz;
+    jmethodID ctorId;
+    jfieldID nameId;
+    jfieldID typeId;
+} gDescriptorInfo;
+
 struct fields_t {
     jmethodID postEventFromNativeID;
     jmethodID lockAndGetContextID;
@@ -922,6 +942,74 @@
     (void)mCodec->setParameters(msg);
 }
 
+status_t JMediaCodec::querySupportedVendorParameters(JNIEnv *env, jobject *namesObj) {
+    std::vector<std::string> names;
+    status_t status = mCodec->querySupportedVendorParameters(&names);
+    if (status != OK) {
+        return status;
+    }
+    *namesObj = env->NewObject(gArrayListInfo.clazz, gArrayListInfo.ctorId);
+    for (const std::string &name : names) {
+        ScopedLocalRef<jstring> nameStr{env, env->NewStringUTF(name.c_str())};
+        (void)env->CallBooleanMethod(*namesObj, gArrayListInfo.addId, nameStr.get());
+    }
+    return OK;
+}
+
+status_t JMediaCodec::describeParameter(JNIEnv *env, jstring name, jobject *descObj) {
+    const char *tmp = env->GetStringUTFChars(name, nullptr);
+    CodecParameterDescriptor desc;
+    status_t status = mCodec->describeParameter(tmp, &desc);
+    env->ReleaseStringUTFChars(name, tmp);
+    if (status != OK) {
+        return status;
+    }
+    jint type = TYPE_NULL;
+    switch (desc.type) {
+        case AMessage::kTypeInt32:  type = TYPE_INTEGER;     break;
+        case AMessage::kTypeSize:
+        case AMessage::kTypeInt64:  type = TYPE_LONG;        break;
+        case AMessage::kTypeFloat:  type = TYPE_FLOAT;       break;
+        case AMessage::kTypeString: type = TYPE_STRING;      break;
+        case AMessage::kTypeBuffer: type = TYPE_BYTE_BUFFER; break;
+        default:                    type = TYPE_NULL;        break;
+    }
+    if (type == TYPE_NULL) {
+        return BAD_VALUE;
+    }
+    *descObj = env->NewObject(gDescriptorInfo.clazz, gDescriptorInfo.ctorId);
+    env->SetObjectField(*descObj, gDescriptorInfo.nameId, name);
+    env->SetIntField(*descObj, gDescriptorInfo.typeId, type);
+    return OK;
+}
+
+static void BuildVectorFromList(JNIEnv *env, jobject list, std::vector<std::string> *vec) {
+    ScopedLocalRef<jclass> listClazz{env, env->FindClass("java/util/List")};
+    ScopedLocalRef<jclass> iterClazz{env, env->FindClass("java/util/Iterator")};
+    jmethodID hasNextID = env->GetMethodID(iterClazz.get(), "hasNext", "()Z");
+    jmethodID nextID = env->GetMethodID(iterClazz.get(), "next", "()Ljava/lang/Object;");
+    jobject it = env->CallObjectMethod(
+            list, env->GetMethodID(listClazz.get(), "iterator", "()Ljava/util/Iterator;"));
+    while (env->CallBooleanMethod(it, hasNextID)) {
+        jstring name = (jstring)env->CallObjectMethod(it, nextID);
+        const char *tmp = env->GetStringUTFChars(name, nullptr);
+        vec->push_back(tmp);
+        env->ReleaseStringUTFChars(name, tmp);
+    }
+}
+
+status_t JMediaCodec::subscribeToVendorParameters(JNIEnv *env, jobject namesObj) {
+    std::vector<std::string> names;
+    BuildVectorFromList(env, namesObj, &names);
+    return mCodec->subscribeToVendorParameters(names);
+}
+
+status_t JMediaCodec::unsubscribeFromVendorParameters(JNIEnv *env, jobject namesObj) {
+    std::vector<std::string> names;
+    BuildVectorFromList(env, namesObj, &names);
+    return mCodec->unsubscribeFromVendorParameters(names);
+}
+
 static jthrowable createCodecException(
         JNIEnv *env, status_t err, int32_t actionCode, const char *msg = NULL) {
     ScopedLocalRef<jclass> clazz(
@@ -2602,6 +2690,73 @@
     codec->selectAudioPresentation((int32_t)presentationId, (int32_t)programId);
 }
 
+static jobject android_media_MediaCodec_getSupportedVendorParameters(
+        JNIEnv *env, jobject thiz) {
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL || codec->initCheck() != OK) {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return NULL;
+    }
+
+    jobject ret = NULL;
+    status_t status = codec->querySupportedVendorParameters(env, &ret);
+    if (status != OK) {
+        throwExceptionAsNecessary(env, status);
+    }
+
+    return ret;
+}
+
+static jobject android_media_MediaCodec_getParameterDescriptor(
+        JNIEnv *env, jobject thiz, jstring name) {
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL || codec->initCheck() != OK) {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return NULL;
+    }
+
+    jobject ret = NULL;
+    status_t status = codec->describeParameter(env, name, &ret);
+    if (status != OK) {
+        ret = NULL;
+    }
+    return ret;
+}
+
+static void android_media_MediaCodec_subscribeToVendorParameters(
+        JNIEnv *env, jobject thiz, jobject names) {
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL || codec->initCheck() != OK) {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return;
+    }
+
+    status_t status = codec->subscribeToVendorParameters(env, names);
+    if (status != OK) {
+        throwExceptionAsNecessary(env, status);
+    }
+    return;
+}
+
+static void android_media_MediaCodec_unsubscribeFromVendorParameters(
+        JNIEnv *env, jobject thiz, jobject names) {
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL || codec->initCheck() != OK) {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return;
+    }
+
+    status_t status = codec->unsubscribeFromVendorParameters(env, names);
+    if (status != OK) {
+        throwExceptionAsNecessary(env, status);
+    }
+    return;
+}
+
 static void android_media_MediaCodec_native_init(JNIEnv *env, jclass) {
     ScopedLocalRef<jclass> clazz(
             env, env->FindClass("android/media/MediaCodec"));
@@ -2861,6 +3016,10 @@
 
     clazz.reset(env->FindClass("java/util/ArrayList"));
     CHECK(clazz.get() != NULL);
+    gArrayListInfo.clazz = (jclass)env->NewGlobalRef(clazz.get());
+
+    gArrayListInfo.ctorId = env->GetMethodID(clazz.get(), "<init>", "()V");
+    CHECK(gArrayListInfo.ctorId != NULL);
 
     gArrayListInfo.sizeId = env->GetMethodID(clazz.get(), "size", "()I");
     CHECK(gArrayListInfo.sizeId != NULL);
@@ -2891,6 +3050,19 @@
 
     gLinearBlockInfo.lockId = env->GetFieldID(clazz.get(), "mLock", "Ljava/lang/Object;");
     CHECK(gLinearBlockInfo.lockId != NULL);
+
+    clazz.reset(env->FindClass("android/media/MediaCodec$ParameterDescriptor"));
+    CHECK(clazz.get() != NULL);
+    gDescriptorInfo.clazz = (jclass)env->NewGlobalRef(clazz.get());
+
+    gDescriptorInfo.ctorId = env->GetMethodID(clazz.get(), "<init>", "()V");
+    CHECK(gDescriptorInfo.ctorId != NULL);
+
+    gDescriptorInfo.nameId = env->GetFieldID(clazz.get(), "mName", "Ljava/lang/String;");
+    CHECK(gDescriptorInfo.nameId != NULL);
+
+    gDescriptorInfo.typeId = env->GetFieldID(clazz.get(), "mType", "I");
+    CHECK(gDescriptorInfo.typeId != NULL);
 }
 
 static void android_media_MediaCodec_native_setup(
@@ -3217,6 +3389,21 @@
     { "native_setAudioPresentation", "(II)V",
       (void *)android_media_MediaCodec_setAudioPresentation },
 
+    { "native_getSupportedVendorParameters", "()Ljava/util/List;",
+      (void *)android_media_MediaCodec_getSupportedVendorParameters },
+
+    { "native_getParameterDescriptor",
+      "(Ljava/lang/String;)Landroid/media/MediaCodec$ParameterDescriptor;",
+      (void *)android_media_MediaCodec_getParameterDescriptor },
+
+    { "native_subscribeToVendorParameters",
+      "(Ljava/util/List;)V",
+      (void *)android_media_MediaCodec_subscribeToVendorParameters},
+
+    { "native_unsubscribeFromVendorParameters",
+      "(Ljava/util/List;)V",
+      (void *)android_media_MediaCodec_unsubscribeFromVendorParameters},
+
     { "native_init", "()V", (void *)android_media_MediaCodec_native_init },
 
     { "native_setup", "(Ljava/lang/String;ZZ)V",
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index a58f9a7..33f481d 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -162,6 +162,14 @@
 
     void selectAudioPresentation(const int32_t presentationId, const int32_t programId);
 
+    status_t querySupportedVendorParameters(JNIEnv *env, jobject *names);
+
+    status_t describeParameter(JNIEnv *env, jstring name, jobject *desc);
+
+    status_t subscribeToVendorParameters(JNIEnv *env, jobject names);
+
+    status_t unsubscribeFromVendorParameters(JNIEnv *env, jobject names);
+
     bool hasCryptoOrDescrambler() { return mHasCryptoOrDescrambler; }
 
 protected:
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 694b939..3976086e 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -287,7 +287,10 @@
 jint Dvr::close() {
     Result r = mDvrSp->close();
     if (r == Result::SUCCESS) {
-        EventFlag::deleteEventFlag(&mDvrMQEventFlag);
+        if (mDvrMQEventFlag != nullptr) {
+            EventFlag::deleteEventFlag(&mDvrMQEventFlag);
+        }
+        mDvrMQ = nullptr;
     }
     return (jint) r;
 }
@@ -723,13 +726,15 @@
 
     env->DeleteWeakGlobalRef(mFilterObj);
     mFilterObj = NULL;
-    EventFlag::deleteEventFlag(&mFilterMQEventFlag);
 }
 
 int Filter::close() {
     Result r = mFilterSp->close();
     if (r == Result::SUCCESS) {
-        EventFlag::deleteEventFlag(&mFilterMQEventFlag);
+        if (mFilterMQEventFlag != nullptr) {
+            EventFlag::deleteEventFlag(&mFilterMQEventFlag);
+        }
+        mFilterMQ = nullptr;
     }
     return (int)r;
 }
@@ -3050,6 +3055,9 @@
             filterSp->mFilterMQ = std::make_unique<MQ>(filterMQDesc, true);
             EventFlag::createEventFlag(
                     filterSp->mFilterMQ->getEventFlagWord(), &(filterSp->mFilterMQEventFlag));
+        } else {
+            filterSp->mFilterMQ = nullptr;
+            filterSp->mFilterMQEventFlag = nullptr;
         }
     }
     return (jint) getQueueDescResult;
@@ -3137,13 +3145,12 @@
 }
 
 static jint android_media_tv_Tuner_close_filter(JNIEnv *env, jobject filter) {
-    sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter();
-    if (iFilterSp == NULL) {
+    sp<Filter> filterSp = getFilter(env, filter);
+    if (filterSp == NULL) {
         ALOGD("Failed to close filter: filter not found");
         return (jint) Result::NOT_INITIALIZED;
     }
-    Result r = iFilterSp->close();
-    return (jint) r;
+    return filterSp->close();
 }
 
 static sp<TimeFilter> getTimeFilter(JNIEnv *env, jobject filter) {
diff --git a/media/jni/soundpool/Sound.cpp b/media/jni/soundpool/Sound.cpp
index f8b4bdb..50e0d33 100644
--- a/media/jni/soundpool/Sound.cpp
+++ b/media/jni/soundpool/Sound.cpp
@@ -99,8 +99,8 @@
                                     __func__);
                             break;
                         }
-                        int sampleSize = AMediaExtractor_readSampleData(ex.get(), buf, bufsize);
-                        ALOGV("%s: read %d", __func__, sampleSize);
+                        ssize_t sampleSize = AMediaExtractor_readSampleData(ex.get(), buf, bufsize);
+                        ALOGV("%s: read %zd", __func__, sampleSize);
                         if (sampleSize < 0) {
                             sampleSize = 0;
                             sawInputEOS = true;
@@ -124,8 +124,8 @@
                 }
 
                 AMediaCodecBufferInfo info;
-                const int status = AMediaCodec_dequeueOutputBuffer(codec.get(), &info, 1);
-                ALOGV("%s: dequeueoutput returned: %d", __func__, status);
+                const ssize_t status = AMediaCodec_dequeueOutputBuffer(codec.get(), &info, 1);
+                ALOGV("%s: dequeueoutput returned: %zd", __func__, status);
                 if (status >= 0) {
                     if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
                         ALOGV("%s: output EOS", __func__);
@@ -167,10 +167,10 @@
                 } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
                     ALOGV("%s: no output buffer right now", __func__);
                 } else if (status <= AMEDIA_ERROR_BASE) {
-                    ALOGE("%s: decode error: %d", __func__, status);
+                    ALOGE("%s: decode error: %zd", __func__, status);
                     break;
                 } else {
-                    ALOGV("%s: unexpected info code: %d", __func__, status);
+                    ALOGV("%s: unexpected info code: %zd", __func__, status);
                 }
             }
 
diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp
index 73e319a..f261bab 100644
--- a/media/jni/soundpool/Stream.cpp
+++ b/media/jni/soundpool/Stream.cpp
@@ -317,7 +317,8 @@
             // audio track while the new one is being started and avoids processing them with
             // wrong audio audio buffer size  (mAudioBufferSize)
             auto toggle = mToggle ^ 1;
-            void* userData = (void*)((uintptr_t)this | toggle);
+            // NOLINTNEXTLINE(performance-no-int-to-ptr)
+            void* userData = reinterpret_cast<void*>((uintptr_t)this | toggle);
             audio_channel_mask_t soundChannelMask = sound->getChannelMask();
             // When sound contains a valid channel mask, use it as is.
             // Otherwise, use stream count to calculate channel mask.
@@ -386,6 +387,7 @@
 void Stream::staticCallback(int event, void* user, void* info)
 {
     const auto userAsInt = (uintptr_t)user;
+    // NOLINTNEXTLINE(performance-no-int-to-ptr)
     auto stream = reinterpret_cast<Stream*>(userAsInt & ~1);
     stream->callback(event, info, int(userAsInt & 1), 0 /* tries */);
 }
diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp
index 502ee00..309d71c 100644
--- a/media/jni/soundpool/StreamManager.cpp
+++ b/media/jni/soundpool/StreamManager.cpp
@@ -43,6 +43,14 @@
 // Amount of time for a StreamManager thread to wait before closing.
 static constexpr int64_t kWaitTimeBeforeCloseNs = 9 * NANOS_PER_SECOND;
 
+// Debug flag:
+// kForceLockStreamManagerStop is set to true to force lock the StreamManager
+// worker thread during stop. This limits concurrency of Stream processing.
+// Normally we lock the StreamManager worker thread during stop ONLY
+// for SoundPools configured with a single Stream.
+//
+static constexpr bool kForceLockStreamManagerStop = false;
+
 ////////////
 
 StreamMap::StreamMap(int32_t streams) {
@@ -103,6 +111,7 @@
     : StreamMap(streams)
     , mAttributes(*attributes)
     , mOpPackageName(std::move(opPackageName))
+    , mLockStreamManagerStop(streams == 1 || kForceLockStreamManagerStop)
 {
     ALOGV("%s(%d, %zu, ...)", __func__, streams, threads);
     forEach([this](Stream *stream) {
@@ -113,7 +122,8 @@
     });
 
     mThreadPool = std::make_unique<ThreadPool>(
-            std::min(threads, (size_t)std::thread::hardware_concurrency()),
+            std::min((size_t)streams,  // do not make more threads than streams to play
+                    std::min(threads, (size_t)std::thread::hardware_concurrency())),
             "SoundPool_");
 }
 
@@ -330,7 +340,7 @@
 
     // streams on mProcessingStreams are undergoing processing by the StreamManager thread
     // and do not participate in normal stream migration.
-    return found;
+    return (ssize_t)found;
 }
 
 void StreamManager::addToRestartQueue_l(Stream *stream) {
@@ -348,14 +358,14 @@
 void StreamManager::run(int32_t id)
 {
     ALOGV("%s(%d) entering", __func__, id);
-    int64_t waitTimeNs = kWaitTimeBeforeCloseNs;
+    int64_t waitTimeNs = 0;  // on thread start, mRestartStreams can be non-empty.
     std::unique_lock lock(mStreamManagerLock);
     while (!mQuit) {
-        if (mRestartStreams.empty()) { // on thread start, mRestartStreams can be non-empty.
+        if (waitTimeNs > 0) {
             mStreamManagerCondition.wait_for(
                     lock, std::chrono::duration<int64_t, std::nano>(waitTimeNs));
         }
-        ALOGV("%s(%d) awake", __func__, id);
+        ALOGV("%s(%d) awake lock waitTimeNs:%lld", __func__, id, (long long)waitTimeNs);
 
         sanityCheckQueue_l();
 
@@ -375,12 +385,12 @@
             }
             mRestartStreams.erase(it);
             mProcessingStreams.emplace(stream);
-            lock.unlock();
+            if (!mLockStreamManagerStop) lock.unlock();
             stream->stop();
             ALOGV("%s(%d) stopping streamID:%d", __func__, id, stream->getStreamID());
             if (Stream* nextStream = stream->playPairStream()) {
                 ALOGV("%s(%d) starting streamID:%d", __func__, id, nextStream->getStreamID());
-                lock.lock();
+                if (!mLockStreamManagerStop) lock.lock();
                 if (nextStream->getStopTimeNs() > 0) {
                     // the next stream was stopped before we can move it to the active queue.
                     ALOGV("%s(%d) stopping started streamID:%d",
@@ -390,7 +400,7 @@
                     addToActiveQueue_l(nextStream);
                 }
             } else {
-                lock.lock();
+                if (!mLockStreamManagerStop) lock.lock();
                 mAvailableStreams.insert(stream);
             }
             mProcessingStreams.erase(stream);
diff --git a/media/jni/soundpool/StreamManager.h b/media/jni/soundpool/StreamManager.h
index 81ac69e..85b468c 100644
--- a/media/jni/soundpool/StreamManager.h
+++ b/media/jni/soundpool/StreamManager.h
@@ -437,6 +437,14 @@
     void sanityCheckQueue_l() const REQUIRES(mStreamManagerLock);
 
     const audio_attributes_t mAttributes;
+    const std::string mOpPackageName;
+
+   // For legacy compatibility, we lock the stream manager on stop when
+   // there is only one stream.  This allows a play to be called immediately
+   // after stopping, otherwise it is possible that the play might be discarded
+   // (returns 0) because that stream may be in the worker thread call to stop.
+    const bool mLockStreamManagerStop;
+
     std::unique_ptr<ThreadPool> mThreadPool;                  // locked internally
 
     // mStreamManagerLock is used to lock access for transitions between the
@@ -477,8 +485,6 @@
     // The paired stream may be active or restarting.
     // No particular order.
     std::unordered_set<Stream*> mProcessingStreams GUARDED_BY(mStreamManagerLock);
-
-    const std::string           mOpPackageName;
 };
 
 } // namespace android::soundpool
diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool.cpp
index 357cc63..a66d99f 100644
--- a/media/jni/soundpool/android_media_SoundPool.cpp
+++ b/media/jni/soundpool/android_media_SoundPool.cpp
@@ -34,7 +34,8 @@
     jclass      mSoundPoolClass;
 } fields;
 static inline SoundPool* MusterSoundPool(JNIEnv *env, jobject thiz) {
-    return (SoundPool*)env->GetLongField(thiz, fields.mNativeContext);
+    // NOLINTNEXTLINE(performance-no-int-to-ptr)
+    return reinterpret_cast<SoundPool*>(env->GetLongField(thiz, fields.mNativeContext));
 }
 static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
 struct audio_attributes_fields_t {
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 6fab9e4..550e324 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -86,7 +86,7 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        mCm = ConnectivityManager.from(this);
+        mCm = getSystemService(ConnectivityManager.class);
         mUrl = getUrlForCaptivePortal();
         if (mUrl == null) {
             done(false);
@@ -161,7 +161,6 @@
         if (network != null) {
             network = network.getPrivateDnsBypassingCopy();
             mCm.bindProcessToNetwork(network);
-            mCm.setProcessDefaultNetworkForHostResolution(network);
         }
         mNetwork = network;
     }
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java
index 78a02d7..43ca739 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java
@@ -49,7 +49,7 @@
             case PROVISION_OBSERVER_REEVALUATION_JOB_ID:
                 if (isProvisioned(this)) {
                     Log.d(TAG, "device provisioned, force network re-evaluation");
-                    final ConnectivityManager connMgr = ConnectivityManager.from(this);
+                    final ConnectivityManager connMgr = getSystemService(ConnectivityManager.class);
                     Network[] info = connMgr.getAllNetworks();
                     for (Network nw : info) {
                         final NetworkCapabilities nc = connMgr.getNetworkCapabilities(nw);
diff --git a/packages/Connectivity/TEST_MAPPING b/packages/Connectivity/TEST_MAPPING
new file mode 100644
index 0000000..94f9232
--- /dev/null
+++ b/packages/Connectivity/TEST_MAPPING
@@ -0,0 +1,19 @@
+{
+  "imports": [
+    {
+      "path": "frameworks/base/core/java/android/net"
+    },
+    {
+      "path": "packages/modules/NetworkStack"
+    },
+    {
+      "path": "packages/modules/CaptivePortalLogin"
+    },
+    {
+      "path": "packages/modules/Connectivity"
+    },
+    {
+      "path": "packages/modules/Connectivity/Tethering"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/Connectivity/framework/Android.bp b/packages/Connectivity/framework/Android.bp
index 9da27d2..017ff51 100644
--- a/packages/Connectivity/framework/Android.bp
+++ b/packages/Connectivity/framework/Android.bp
@@ -23,7 +23,25 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
-// TODO: use a java_library in the bootclasspath instead
+java_library {
+    name: "framework-connectivity-protos",
+    proto: {
+        type: "nano",
+    },
+    srcs: [
+        // TODO: consider moving relevant .proto files directly to the module directory
+        ":framework-javastream-protos",
+    ],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.tethering",
+    ],
+    jarjar_rules: "jarjar-rules-proto.txt",
+    visibility: [
+        "//visibility:private",
+    ],
+}
+
 filegroup {
     name: "framework-connectivity-internal-sources",
     srcs: [
@@ -84,3 +102,39 @@
     ],
     permitted_packages: ["android.net", "com.android.connectivity.aidl"],
 }
+
+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",
+    srcs: [
+        ":framework-connectivity-sources",
+    ],
+    aidl: {
+        include_dirs: [
+            "frameworks/base/core/java", // For framework parcelables
+            "frameworks/native/aidl/binder", // For PersistableBundle.aidl
+        ],
+    },
+    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",
+        "unsupportedappusage",
+        "ServiceConnectivityResources",
+    ],
+    static_libs: [
+        "framework-connectivity-protos",
+        "net-utils-device-common",
+    ],
+    jarjar_rules: "jarjar-rules.txt",
+    apex_available: ["com.android.tethering"],
+    installable: true,
+    permitted_packages: ["android.net", "com.android.connectivity.aidl"],
+}
diff --git a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl b/packages/Connectivity/framework/aidl-export/android/net/NetworkScore.aidl
similarity index 79%
copy from packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
copy to packages/Connectivity/framework/aidl-export/android/net/NetworkScore.aidl
index 7979afc..af12dcf 100644
--- a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/NetworkScore.aidl
@@ -1,6 +1,5 @@
 /**
- *
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -17,7 +16,5 @@
 
 package android.net;
 
-/** @hide */
-oneway interface IOnSetOemNetworkPreferenceListener {
-    void onComplete();
-}
+parcelable NetworkScore;
+
diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt
index a8f1a4d..ad44b27 100644
--- a/packages/Connectivity/framework/api/current.txt
+++ b/packages/Connectivity/framework/api/current.txt
@@ -87,6 +87,7 @@
     method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public boolean isActiveNetworkMetered();
     method public boolean isDefaultNetworkActive();
     method @Deprecated public static boolean isNetworkTypeValid(int);
+    method public void registerBestMatchingNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback);
     method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback);
@@ -143,6 +144,7 @@
 
   public static class ConnectivityManager.NetworkCallback {
     ctor public ConnectivityManager.NetworkCallback();
+    ctor public ConnectivityManager.NetworkCallback(int);
     method public void onAvailable(@NonNull android.net.Network);
     method public void onBlockedStatusChanged(@NonNull android.net.Network, boolean);
     method public void onCapabilitiesChanged(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities);
@@ -150,6 +152,7 @@
     method public void onLosing(@NonNull android.net.Network, int);
     method public void onLost(@NonNull android.net.Network);
     method public void onUnavailable();
+    field public static final int FLAG_INCLUDE_LOCATION_INFO = 1; // 0x1
   }
 
   public static interface ConnectivityManager.OnNetworkActiveListener {
@@ -288,11 +291,13 @@
     ctor public NetworkCapabilities();
     ctor public NetworkCapabilities(android.net.NetworkCapabilities);
     method public int describeContents();
+    method @NonNull public int[] getCapabilities();
     method public int getLinkDownstreamBandwidthKbps();
     method public int getLinkUpstreamBandwidthKbps();
     method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
     method public int getOwnerUid();
     method public int getSignalStrength();
+    method @NonNull public java.util.Set<java.lang.Integer> getSubIds();
     method @Nullable public android.net.TransportInfo getTransportInfo();
     method public boolean hasCapability(int);
     method public boolean hasTransport(int);
@@ -391,6 +396,7 @@
 
   public static class NetworkRequest.Builder {
     ctor public NetworkRequest.Builder();
+    ctor public NetworkRequest.Builder(@NonNull android.net.NetworkRequest);
     method public android.net.NetworkRequest.Builder addCapability(int);
     method public android.net.NetworkRequest.Builder addTransportType(int);
     method public android.net.NetworkRequest build();
@@ -399,6 +405,11 @@
     method public android.net.NetworkRequest.Builder removeTransportType(int);
     method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String);
     method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
+    method @NonNull public android.net.NetworkRequest.Builder setSubIds(@NonNull java.util.Set<java.lang.Integer>);
+  }
+
+  public class ParseException extends java.lang.RuntimeException {
+    field public String response;
   }
 
   public class ProxyInfo implements android.os.Parcelable {
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index d2ed73e..35e45ec 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -6,24 +6,134 @@
   }
 
   public class ConnectivityManager {
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void factoryReset();
     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> getAllNetworkStateSnapshot();
+    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 registerDefaultNetworkCallbackAsUid(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, @Nullable android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
+    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.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
+    method @Deprecated public boolean requestRouteToHostAddress(int, java.net.InetAddress);
+    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 setAcceptPartialConnectivity(@NonNull android.net.Network, boolean, boolean);
+    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 setAcceptUnvalidated(@NonNull android.net.Network, boolean, boolean);
+    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);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void startCaptivePortalApp(@NonNull android.net.Network);
+    method public void systemReady();
+    field public static final int BLOCKED_METERED_REASON_ADMIN_DISABLED = 262144; // 0x40000
+    field public static final int BLOCKED_METERED_REASON_DATA_SAVER = 65536; // 0x10000
+    field public static final int BLOCKED_METERED_REASON_MASK = -65536; // 0xffff0000
+    field public static final int BLOCKED_METERED_REASON_USER_RESTRICTED = 131072; // 0x20000
+    field public static final int BLOCKED_REASON_APP_STANDBY = 4; // 0x4
+    field public static final int BLOCKED_REASON_BATTERY_SAVER = 1; // 0x1
+    field public static final int BLOCKED_REASON_DOZE = 2; // 0x2
+    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
+  }
+
+  public static class ConnectivityManager.NetworkCallback {
+    method public void onBlockedStatusChanged(@NonNull android.net.Network, int);
+  }
+
+  public class ConnectivitySettingsManager {
+    method public static void clearGlobalProxy(@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);
+    method @NonNull public static android.util.Range<java.lang.Integer> getDnsResolverSampleRanges(@NonNull android.content.Context);
+    method @NonNull public static java.time.Duration getDnsResolverSampleValidityDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
+    method public static int getDnsResolverSuccessThresholdPercent(@NonNull android.content.Context, int);
+    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 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 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 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);
+    method public static void setDnsResolverSampleRanges(@NonNull android.content.Context, @NonNull android.util.Range<java.lang.Integer>);
+    method public static void setDnsResolverSampleValidityDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
+    method public static void setDnsResolverSuccessThresholdPercent(@NonNull android.content.Context, @IntRange(from=0, to=100) int);
+    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 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 setPrivateDnsHostname(@NonNull android.content.Context, @Nullable String);
+    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
+    field public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0; // 0x0
+    field public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1; // 0x1
+    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
   }
 
   public final class NetworkAgentConfig implements android.os.Parcelable {
     method @Nullable public String getSubscriberId();
+    method public boolean isBypassableVpn();
   }
 
   public static final class NetworkAgentConfig.Builder {
+    method @NonNull public android.net.NetworkAgentConfig.Builder setBypassableVpn(boolean);
     method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String);
   }
 
   public final class NetworkCapabilities implements android.os.Parcelable {
+    ctor public NetworkCapabilities(@Nullable android.net.NetworkCapabilities, long);
+    method @Nullable public java.util.Set<android.util.Range<java.lang.Integer>> getUids();
+    method public boolean hasUnwantedCapability(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
+    field public static final long REDACT_FOR_NETWORK_SETTINGS = 4L; // 0x4L
+    field public static final long REDACT_NONE = 0L; // 0x0L
     field public static final int TRANSPORT_TEST = 7; // 0x7
   }
 
+  public static final class NetworkCapabilities.Builder {
+    method @NonNull public android.net.NetworkCapabilities.Builder setUids(@Nullable java.util.Set<android.util.Range<java.lang.Integer>>);
+  }
+
+  public class NetworkRequest implements android.os.Parcelable {
+    method public boolean hasUnwantedCapability(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 setUids(@Nullable java.util.Set<android.util.Range<java.lang.Integer>>);
+  }
+
+  public class ParseException extends java.lang.RuntimeException {
+    ctor public ParseException(@NonNull String);
+    ctor public ParseException(@NonNull String, @NonNull Throwable);
+  }
+
   public final class TcpRepairWindow {
     ctor public TcpRepairWindow(int, int, int, int, int, int);
     field public final int maxWindow;
@@ -51,6 +161,19 @@
     field public static final String TEST_TAP_PREFIX = "testtap";
   }
 
+  public final class TestNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
+    ctor public TestNetworkSpecifier(@NonNull String);
+    method public int describeContents();
+    method @Nullable public String getInterfaceName();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.TestNetworkSpecifier> CREATOR;
+  }
+
+  public interface TransportInfo {
+    method public default long getApplicableRedactions();
+    method @NonNull public default android.net.TransportInfo makeCopy(long);
+  }
+
   public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo {
     ctor public VpnTransportInfo(int);
     method public int describeContents();
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
index a732430..6f832c6 100644
--- a/packages/Connectivity/framework/api/system-current.txt
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -18,7 +18,7 @@
     method public long getRefreshTimeMillis();
     method @Nullable public android.net.Uri getUserPortalUrl();
     method public int getUserPortalUrlSource();
-    method @Nullable public String getVenueFriendlyName();
+    method @Nullable public CharSequence getVenueFriendlyName();
     method @Nullable public android.net.Uri getVenueInfoUrl();
     method public int getVenueInfoUrlSource();
     method public boolean isCaptive();
@@ -40,7 +40,7 @@
     method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean);
     method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri);
     method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri, int);
-    method @NonNull public android.net.CaptivePortalData.Builder setVenueFriendlyName(@Nullable String);
+    method @NonNull public android.net.CaptivePortalData.Builder setVenueFriendlyName(@Nullable CharSequence);
     method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri);
     method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri, int);
   }
@@ -52,11 +52,11 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
-    method public void registerQosCallback(@NonNull android.net.QosSocketInfo, @NonNull android.net.QosCallback, @NonNull java.util.concurrent.Executor);
+    method public void registerQosCallback(@NonNull android.net.QosSocketInfo, @NonNull java.util.concurrent.Executor, @NonNull android.net.QosCallback);
     method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
     method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void requestNetwork(@NonNull android.net.NetworkRequest, int, int, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
-    method @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE) public void setOemNetworkPreference(@NonNull android.net.OemNetworkPreferences, @Nullable java.util.concurrent.Executor, @Nullable android.net.ConnectivityManager.OnSetOemNetworkPreferenceListener);
+    method @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE) public void setOemNetworkPreference(@NonNull android.net.OemNetworkPreferences, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi();
     method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
     method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
@@ -78,10 +78,6 @@
     field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd
   }
 
-  public static interface ConnectivityManager.OnSetOemNetworkPreferenceListener {
-    method public void onComplete();
-  }
-
   @Deprecated public abstract static class ConnectivityManager.OnStartTetheringCallback {
     ctor @Deprecated public ConnectivityManager.OnStartTetheringCallback();
     method @Deprecated public void onTetheringFailed();
@@ -216,10 +212,13 @@
 
   public abstract class NetworkAgent {
     ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, int, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider);
+    ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkScore, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider);
     method @Nullable public android.net.Network getNetwork();
     method public void markConnected();
     method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
     method public void onAutomaticReconnectDisabled();
+    method public void onNetworkCreated();
+    method public void onNetworkDisconnected();
     method public void onNetworkUnwanted();
     method public void onQosCallbackRegistered(int, @NonNull android.net.QosFilter);
     method public void onQosCallbackUnregistered(int);
@@ -234,8 +233,8 @@
     method public final void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
     method public final void sendNetworkScore(@IntRange(from=0, to=99) int);
     method public final void sendQosCallbackError(int, int);
-    method public final void sendQosSessionAvailable(int, int, @NonNull android.telephony.data.EpsBearerQosSessionAttributes);
-    method public final void sendQosSessionLost(int, int);
+    method public final void sendQosSessionAvailable(int, int, @NonNull android.net.QosSessionAttributes);
+    method public final void sendQosSessionLost(int, int, int);
     method public final void sendSocketKeepaliveEvent(int, int);
     method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
     method public void unregister();
@@ -265,7 +264,6 @@
   }
 
   public final class NetworkCapabilities implements android.os.Parcelable {
-    ctor public NetworkCapabilities(@Nullable android.net.NetworkCapabilities, boolean);
     method @NonNull public int[] getAdministratorUids();
     method @Nullable public String getSsid();
     method @NonNull public int[] getTransportTypes();
@@ -296,6 +294,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorUid(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkCapabilities.Builder setSignalStrength(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String);
+    method @NonNull public android.net.NetworkCapabilities.Builder setSubIds(@NonNull java.util.Set<java.lang.Integer>);
     method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo);
   }
 
@@ -320,6 +319,19 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int);
   }
 
+  public final class NetworkScore implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getLegacyInt();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkScore> CREATOR;
+  }
+
+  public static final class NetworkScore.Builder {
+    ctor public NetworkScore.Builder();
+    method @NonNull public android.net.NetworkScore build();
+    method @NonNull public android.net.NetworkScore.Builder setLegacyInt(int);
+  }
+
   public final class OemNetworkPreferences implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public java.util.Map<java.lang.String,java.lang.Integer> getNetworkPreferences();
@@ -367,6 +379,7 @@
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.QosSession> CREATOR;
     field public static final int TYPE_EPS_BEARER = 1; // 0x1
+    field public static final int TYPE_NR_BEARER = 2; // 0x2
   }
 
   public interface QosSessionAttributes {
@@ -438,11 +451,6 @@
     field public final int tcpWindowScale;
   }
 
-  public interface TransportInfo {
-    method public default boolean hasLocationSensitiveFields();
-    method @NonNull public default android.net.TransportInfo makeCopy(boolean);
-  }
-
 }
 
 package android.net.apf {
diff --git a/packages/Connectivity/framework/jarjar-rules-proto.txt b/packages/Connectivity/framework/jarjar-rules-proto.txt
new file mode 100644
index 0000000..37b4dec1
--- /dev/null
+++ b/packages/Connectivity/framework/jarjar-rules-proto.txt
@@ -0,0 +1,3 @@
+keep android.net.NetworkCapabilitiesProto
+keep android.net.NetworkProto
+keep android.net.NetworkRequestProto
diff --git a/packages/Connectivity/framework/jarjar-rules.txt b/packages/Connectivity/framework/jarjar-rules.txt
new file mode 100644
index 0000000..0959840
--- /dev/null
+++ b/packages/Connectivity/framework/jarjar-rules.txt
@@ -0,0 +1,10 @@
+rule com.android.net.module.util.** android.net.connectivity.framework.util.@1
+
+# TODO (b/149403767): remove the annotations from net-utils-device-common instead of here
+zap android.annotation.**
+zap com.android.net.module.annotation.**
+zap com.android.internal.annotations.**
+
+rule android.net.NetworkCapabilitiesProto* android.net.connectivity.proto.NetworkCapabilitiesProto@1
+rule android.net.NetworkProto* android.net.connectivity.proto.NetworkProto@1
+rule android.net.NetworkRequestProto* android.net.connectivity.proto.NetworkRequestProto@1
diff --git a/packages/Connectivity/framework/src/android/net/CaptivePortalData.java b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java
index eafda4d..82dbd0f 100644
--- a/packages/Connectivity/framework/src/android/net/CaptivePortalData.java
+++ b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java
@@ -42,7 +42,7 @@
     private final long mByteLimit;
     private final long mExpiryTimeMillis;
     private final boolean mCaptive;
-    private final String mVenueFriendlyName;
+    private final CharSequence mVenueFriendlyName;
     private final int mVenueInfoUrlSource;
     private final int mUserPortalUrlSource;
 
@@ -65,7 +65,7 @@
 
     private CaptivePortalData(long refreshTimeMillis, Uri userPortalUrl, Uri venueInfoUrl,
             boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive,
-            String venueFriendlyName, int venueInfoUrlSource, int userPortalUrlSource) {
+            CharSequence venueFriendlyName, int venueInfoUrlSource, int userPortalUrlSource) {
         mRefreshTimeMillis = refreshTimeMillis;
         mUserPortalUrl = userPortalUrl;
         mVenueInfoUrl = venueInfoUrl;
@@ -80,7 +80,7 @@
 
     private CaptivePortalData(Parcel p) {
         this(p.readLong(), p.readParcelable(null), p.readParcelable(null), p.readBoolean(),
-                p.readLong(), p.readLong(), p.readBoolean(), p.readString(), p.readInt(),
+                p.readLong(), p.readLong(), p.readBoolean(), p.readCharSequence(), p.readInt(),
                 p.readInt());
     }
 
@@ -98,7 +98,7 @@
         dest.writeLong(mByteLimit);
         dest.writeLong(mExpiryTimeMillis);
         dest.writeBoolean(mCaptive);
-        dest.writeString(mVenueFriendlyName);
+        dest.writeCharSequence(mVenueFriendlyName);
         dest.writeInt(mVenueInfoUrlSource);
         dest.writeInt(mUserPortalUrlSource);
     }
@@ -114,7 +114,7 @@
         private long mBytesRemaining = -1;
         private long mExpiryTime = -1;
         private boolean mCaptive;
-        private String mVenueFriendlyName;
+        private CharSequence mVenueFriendlyName;
         private @CaptivePortalDataSource int mVenueInfoUrlSource = CAPTIVE_PORTAL_DATA_SOURCE_OTHER;
         private @CaptivePortalDataSource int mUserPortalUrlSource =
                 CAPTIVE_PORTAL_DATA_SOURCE_OTHER;
@@ -228,7 +228,7 @@
          * Set the venue friendly name.
          */
         @NonNull
-        public Builder setVenueFriendlyName(@Nullable String venueFriendlyName) {
+        public Builder setVenueFriendlyName(@Nullable CharSequence venueFriendlyName) {
             mVenueFriendlyName = venueFriendlyName;
             return this;
         }
@@ -321,7 +321,7 @@
      * Get the venue friendly name
      */
     @Nullable
-    public String getVenueFriendlyName() {
+    public CharSequence getVenueFriendlyName() {
         return mVenueFriendlyName;
     }
 
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.java
index 5234494..3598ebc 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.java
@@ -28,7 +28,6 @@
 import android.os.RemoteException;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -70,8 +69,8 @@
 
     /** @hide */
     public ConnectivityDiagnosticsManager(Context context, IConnectivityManager service) {
-        mContext = Preconditions.checkNotNull(context, "missing context");
-        mService = Preconditions.checkNotNull(service, "missing IConnectivityManager");
+        mContext = Objects.requireNonNull(context, "missing context");
+        mService = Objects.requireNonNull(service, "missing IConnectivityManager");
     }
 
     /** @hide */
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 45ed317..d196c1a 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -16,9 +16,11 @@
 package android.net;
 
 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
-import static android.net.IpSecManager.INVALID_RESOURCE_ID;
+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;
 import static android.net.NetworkRequest.Type.REQUEST;
 import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
 import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT;
@@ -31,11 +33,15 @@
 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;
 import android.app.PendingIntent;
+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.IpSecManager.UdpEncapsulationSocket;
@@ -43,6 +49,7 @@
 import android.net.TetheringManager.StartTetheringCallback;
 import android.net.TetheringManager.TetheringEventCallback;
 import android.net.TetheringManager.TetheringRequest;
+import android.net.wifi.WifiNetworkSuggestion;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
@@ -57,20 +64,18 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
-import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
+import android.os.UserHandle;
 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;
 import android.util.SparseIntArray;
 
-import com.android.connectivity.aidl.INetworkAgent;
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
-import com.android.internal.util.Protocol;
 
 import libcore.net.event.NetworkEventDispatcher;
 
@@ -801,24 +806,134 @@
     /**
      * @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 {}
+
     /**
-     * The default Private DNS mode.
-     *
-     * This may change from release to release or may become dependent upon
-     * the capabilities of the underlying platform.
+     * Flag to indicate that an app is not subject to any restrictions that could result in its
+     * network access blocked.
      *
      * @hide
      */
-    public static final String PRIVATE_DNS_DEFAULT_MODE_FALLBACK = PRIVATE_DNS_MODE_OPPORTUNISTIC;
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final int BLOCKED_REASON_NONE = 0;
+
+    /**
+     * Flag to indicate that an app is subject to Battery saver restrictions that would
+     * result in its network access being blocked.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final int BLOCKED_REASON_BATTERY_SAVER = 1 << 0;
+
+    /**
+     * Flag to indicate that an app is subject to Doze restrictions that would
+     * result in its network access being blocked.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final int BLOCKED_REASON_DOZE = 1 << 1;
+
+    /**
+     * Flag to indicate that an app is subject to App Standby restrictions that would
+     * result in its network access being blocked.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final int BLOCKED_REASON_APP_STANDBY = 1 << 2;
+
+    /**
+     * Flag to indicate that an app is subject to Restricted mode restrictions that would
+     * result in its network access being blocked.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final int BLOCKED_REASON_RESTRICTED_MODE = 1 << 3;
+
+    /**
+     * Flag to indicate that an app is blocked because it is subject to an always-on VPN but the VPN
+     * is not currently connected.
+     *
+     * @see DevicePolicyManager#setAlwaysOnVpnPackage(ComponentName, String, boolean)
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final int BLOCKED_REASON_LOCKDOWN_VPN = 1 << 4;
+
+    /**
+     * Flag to indicate that an app is subject to Data saver restrictions that would
+     * result in its metered network access being blocked.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final int BLOCKED_METERED_REASON_DATA_SAVER = 1 << 16;
+
+    /**
+     * Flag to indicate that an app is subject to user restrictions that would
+     * result in its metered network access being blocked.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final int BLOCKED_METERED_REASON_USER_RESTRICTED = 1 << 17;
+
+    /**
+     * Flag to indicate that an app is subject to Device admin restrictions that would
+     * result in its metered network access being blocked.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final int BLOCKED_METERED_REASON_ADMIN_DISABLED = 1 << 18;
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = {"BLOCKED_"}, value = {
+            BLOCKED_REASON_NONE,
+            BLOCKED_REASON_BATTERY_SAVER,
+            BLOCKED_REASON_DOZE,
+            BLOCKED_REASON_APP_STANDBY,
+            BLOCKED_REASON_RESTRICTED_MODE,
+            BLOCKED_METERED_REASON_DATA_SAVER,
+            BLOCKED_METERED_REASON_USER_RESTRICTED,
+            BLOCKED_METERED_REASON_ADMIN_DISABLED,
+    })
+    public @interface BlockedReason {}
+
+    /**
+     * Set of blocked reasons that are only applicable on metered networks.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final int BLOCKED_METERED_REASON_MASK = 0xffff0000;
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
     private final IConnectivityManager mService;
@@ -833,7 +948,6 @@
 
     private final Context mContext;
 
-    private INetworkPolicyManager mNPManager;
     private final TetheringManager mTetheringManager;
 
     /**
@@ -906,8 +1020,8 @@
 
     /**
      * @hide
-     * TODO: Expose for SystemServer when becomes a module.
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public void systemReady() {
         try {
             mService.systemReady();
@@ -962,6 +1076,33 @@
     }
 
     /**
+     * Preference for {@link #setNetworkPreferenceForUser(UserHandle, int, Executor, Runnable)}.
+     * Specify that the traffic for this user should by follow the default rules.
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int PROFILE_NETWORK_PREFERENCE_DEFAULT = 0;
+
+    /**
+     * Preference for {@link #setNetworkPreferenceForUser(UserHandle, int, Executor, Runnable)}.
+     * Specify that the traffic for this user should by default go on a network with
+     * {@link NetworkCapabilities#NET_CAPABILITY_ENTERPRISE}, and on the system default network
+     * if no such network is available.
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            PROFILE_NETWORK_PREFERENCE_DEFAULT,
+            PROFILE_NETWORK_PREFERENCE_ENTERPRISE
+    })
+    public @interface ProfileNetworkPreference {
+    }
+
+    /**
      * Specifies the preferred network type.  When the device has more
      * than one type available the preferred network type will be used.
      *
@@ -1090,12 +1231,13 @@
      * @param ranges the UID ranges to restrict
      * @param requireVpn whether the specified UID ranges must use a VPN
      *
-     * TODO: expose as @SystemApi.
      * @hide
      */
     @RequiresPermission(anyOf = {
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
-            android.Manifest.permission.NETWORK_STACK})
+            android.Manifest.permission.NETWORK_STACK,
+            android.Manifest.permission.NETWORK_SETTINGS})
+    @SystemApi(client = MODULE_LIBRARIES)
     public void setRequireVpnForUids(boolean requireVpn,
             @NonNull Collection<Range<Integer>> ranges) {
         Objects.requireNonNull(ranges);
@@ -1139,13 +1281,13 @@
      *
      * @param enabled whether legacy lockdown VPN is enabled or disabled
      *
-     * TODO: @SystemApi(client = MODULE_LIBRARIES)
-     *
      * @hide
      */
     @RequiresPermission(anyOf = {
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK,
             android.Manifest.permission.NETWORK_SETTINGS})
+    @SystemApi(client = MODULE_LIBRARIES)
     public void setLegacyLockdownVpnEnabled(boolean enabled) {
         try {
             mService.setLegacyLockdownVpnEnabled(enabled);
@@ -1314,7 +1456,7 @@
     }
 
     /**
-     * Returns an array of {@link android.net.NetworkCapabilities} objects, representing
+     * Returns an array of {@link NetworkCapabilities} objects, representing
      * the Networks that applications run by the given user will use by default.
      * @hide
      */
@@ -1394,11 +1536,19 @@
     }
 
     /**
-     * Get the {@link android.net.NetworkCapabilities} for the given {@link Network}.  This
+     * Get the {@link NetworkCapabilities} for the given {@link Network}.  This
      * will return {@code null} if the network is unknown.
      *
+     * This will remove any location sensitive data in {@link TransportInfo} embedded in
+     * {@link NetworkCapabilities#getTransportInfo()}. Some transport info instances like
+     * {@link android.net.wifi.WifiInfo} contain location sensitive information. Retrieving
+     * this location sensitive information (subject to app's location permissions) will be
+     * noted by system. To include any location sensitive data in {@link TransportInfo},
+     * use a {@link NetworkCallback} with
+     * {@link NetworkCallback#FLAG_INCLUDE_LOCATION_INFO} flag.
+     *
      * @param network The {@link Network} object identifying the network in question.
-     * @return The {@link android.net.NetworkCapabilities} for the network, or {@code null}.
+     * @return The {@link NetworkCapabilities} for the network, or {@code null}.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
     @Nullable
@@ -1733,7 +1883,9 @@
         // Map from type to transports.
         final int NOT_FOUND = -1;
         final int transport = sLegacyTypeToTransport.get(type, NOT_FOUND);
-        Preconditions.checkArgument(transport != NOT_FOUND, "unknown legacy type: " + type);
+        if (transport == NOT_FOUND) {
+            throw new IllegalArgumentException("unknown legacy type: " + type);
+        }
         nc.addTransportType(transport);
 
         // Map from type to capabilities.
@@ -1838,8 +1990,8 @@
         }
 
         private PacketKeepalive(Network network, PacketKeepaliveCallback callback) {
-            Preconditions.checkNotNull(network, "network cannot be null");
-            Preconditions.checkNotNull(callback, "callback cannot be null");
+            Objects.requireNonNull(network, "network cannot be null");
+            Objects.requireNonNull(callback, "callback cannot be null");
             mNetwork = network;
             mExecutor = Executors.newSingleThreadExecutor();
             mCallback = new ISocketKeepaliveCallback.Stub() {
@@ -1996,7 +2148,7 @@
             dup = createInvalidFd();
         }
         return new NattSocketKeepalive(mService, network, dup,
-                INVALID_RESOURCE_ID /* Unused */, source, destination, executor, callback);
+                -1 /* Unused */, source, destination, executor, callback);
     }
 
     /**
@@ -2082,6 +2234,7 @@
      */
     @Deprecated
     @UnsupportedAppUsage
+    @SystemApi(client = MODULE_LIBRARIES)
     public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
         checkLegacyRoutingApiAccess();
         try {
@@ -2214,7 +2367,9 @@
      */
     public void removeDefaultNetworkActiveListener(@NonNull OnNetworkActiveListener l) {
         INetworkActivityListener rl = mNetworkActivityListeners.get(l);
-        Preconditions.checkArgument(rl != null, "Listener was not registered.");
+        if (rl == null) {
+            throw new IllegalArgumentException("Listener was not registered.");
+        }
         try {
             mService.registerNetworkActivityListener(rl);
         } catch (RemoteException e) {
@@ -2242,8 +2397,8 @@
      * {@hide}
      */
     public ConnectivityManager(Context context, IConnectivityManager service) {
-        mContext = Preconditions.checkNotNull(context, "missing context");
-        mService = Preconditions.checkNotNull(service, "missing IConnectivityManager");
+        mContext = Objects.requireNonNull(context, "missing context");
+        mService = Objects.requireNonNull(service, "missing IConnectivityManager");
         mTetheringManager = (TetheringManager) mContext.getSystemService(Context.TETHERING_SERVICE);
         sInstance = this;
     }
@@ -2510,7 +2665,7 @@
     @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
     public void startTethering(int type, boolean showProvisioningUi,
             final OnStartTetheringCallback callback, Handler handler) {
-        Preconditions.checkNotNull(callback, "OnStartTetheringCallback cannot be null.");
+        Objects.requireNonNull(callback, "OnStartTetheringCallback cannot be null.");
 
         final Executor executor = new Executor() {
             @Override
@@ -2603,7 +2758,7 @@
     public void registerTetheringEventCallback(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull final OnTetheringEventCallback callback) {
-        Preconditions.checkNotNull(callback, "OnTetheringEventCallback cannot be null.");
+        Objects.requireNonNull(callback, "OnTetheringEventCallback cannot be null.");
 
         final TetheringEventCallback tetherCallback =
                 new TetheringEventCallback() {
@@ -2901,7 +3056,7 @@
     public void getLatestTetheringEntitlementResult(int type, boolean showEntitlementUi,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull final OnTetheringEntitlementResultListener listener) {
-        Preconditions.checkNotNull(listener, "TetheringEntitlementResultListener cannot be null.");
+        Objects.requireNonNull(listener, "TetheringEntitlementResultListener cannot be null.");
         ResultReceiver wrappedListener = new ResultReceiver(null) {
             @Override
             protected void onReceiveResult(int resultCode, Bundle resultData) {
@@ -2994,8 +3149,9 @@
      *        HTTP proxy.  A {@code null} value will clear the global HTTP proxy.
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
-    public void setGlobalProxy(ProxyInfo p) {
+    public void setGlobalProxy(@Nullable ProxyInfo p) {
         try {
             mService.setGlobalProxy(p);
         } catch (RemoteException e) {
@@ -3010,6 +3166,8 @@
      *        if no global HTTP proxy is set.
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @Nullable
     public ProxyInfo getGlobalProxy() {
         try {
             return mService.getGlobalProxy();
@@ -3196,10 +3354,6 @@
         }
     }
 
-    // TODO : remove this method. It is a stopgap measure to help sheperding a number
-    // of dependent changes that would conflict throughout the automerger graph. Having this
-    // temporarily helps with the process of going through with all these dependent changes across
-    // the entire tree.
     /**
      * @hide
      * Register a NetworkAgent with ConnectivityService.
@@ -3209,20 +3363,8 @@
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
             android.Manifest.permission.NETWORK_FACTORY})
     public Network registerNetworkAgent(INetworkAgent na, NetworkInfo ni, LinkProperties lp,
-            NetworkCapabilities nc, int score, NetworkAgentConfig config) {
-        return registerNetworkAgent(na, ni, lp, nc, score, config, NetworkProvider.ID_NONE);
-    }
-
-    /**
-     * @hide
-     * Register a NetworkAgent with ConnectivityService.
-     * @return Network corresponding to NetworkAgent.
-     */
-    @RequiresPermission(anyOf = {
-            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
-            android.Manifest.permission.NETWORK_FACTORY})
-    public Network registerNetworkAgent(INetworkAgent na, NetworkInfo ni, LinkProperties lp,
-            NetworkCapabilities nc, int score, NetworkAgentConfig config, int providerId) {
+            NetworkCapabilities nc, @NonNull NetworkScore score, NetworkAgentConfig config,
+            int providerId) {
         try {
             return mService.registerNetworkAgent(na, ni, lp, nc, score, config, providerId);
         } catch (RemoteException e) {
@@ -3244,6 +3386,56 @@
      */
     public static class NetworkCallback {
         /**
+         * No flags associated with this callback.
+         * @hide
+         */
+        public static final int FLAG_NONE = 0;
+        /**
+         * Use this flag to include any location sensitive data in {@link NetworkCapabilities} sent
+         * via {@link #onCapabilitiesChanged(Network, NetworkCapabilities)}.
+         * <p>
+         * These include:
+         * <li> Some transport info instances (retrieved via
+         * {@link NetworkCapabilities#getTransportInfo()}) like {@link android.net.wifi.WifiInfo}
+         * contain location sensitive information.
+         * <li> OwnerUid (retrieved via {@link NetworkCapabilities#getOwnerUid()} is location
+         * sensitive for wifi suggestor apps (i.e using {@link WifiNetworkSuggestion}).</li>
+         * </p>
+         * <p>
+         * Note:
+         * <li> Retrieving this location sensitive information (subject to app's location
+         * permissions) will be noted by system. </li>
+         * <li> Without this flag any {@link NetworkCapabilities} provided via the callback does
+         * not include location sensitive info.
+         * </p>
+         */
+        public static final int FLAG_INCLUDE_LOCATION_INFO = 1 << 0;
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(flag = true, prefix = "FLAG_", value = {
+                FLAG_NONE,
+                FLAG_INCLUDE_LOCATION_INFO
+        })
+        public @interface Flag { }
+
+        /**
+         * All the valid flags for error checking.
+         */
+        private static final int VALID_FLAGS = FLAG_INCLUDE_LOCATION_INFO;
+
+        public NetworkCallback() {
+            this(FLAG_NONE);
+        }
+
+        public NetworkCallback(@Flag int flags) {
+            if ((flags & VALID_FLAGS) != flags) {
+                throw new IllegalArgumentException("Invalid flags");
+            }
+            mFlags = flags;
+        }
+
+        /**
          * Called when the framework connects to a new network to evaluate whether it satisfies this
          * request. If evaluation succeeds, this callback may be followed by an {@link #onAvailable}
          * callback. There is no guarantee that this new network will satisfy any requests, or that
@@ -3271,12 +3463,30 @@
          * @param blocked Whether access to the {@link Network} is blocked due to system policy.
          * @hide
          */
-        public void onAvailable(@NonNull Network network,
+        public final void onAvailable(@NonNull Network network,
                 @NonNull NetworkCapabilities networkCapabilities,
-                @NonNull LinkProperties linkProperties, boolean blocked) {
+                @NonNull LinkProperties linkProperties, @BlockedReason int blocked) {
             // Internally only this method is called when a new network is available, and
             // it calls the callback in the same way and order that older versions used
             // to call so as not to change the behavior.
+            onAvailable(network, networkCapabilities, linkProperties, blocked != 0);
+            onBlockedStatusChanged(network, blocked);
+        }
+
+        /**
+         * Legacy variant of onAvailable that takes a boolean blocked reason.
+         *
+         * This method has never been public API, but it's not final, so there may be apps that
+         * implemented it and rely on it being called. Do our best not to break them.
+         * Note: such apps will also get a second call to onBlockedStatusChanged immediately after
+         * this method is called. There does not seem to be a way to avoid this.
+         * TODO: add a compat check to move apps off this method, and eventually stop calling it.
+         *
+         * @hide
+         */
+        public void onAvailable(@NonNull Network network,
+                @NonNull NetworkCapabilities networkCapabilities,
+                @NonNull LinkProperties linkProperties, boolean blocked) {
             onAvailable(network);
             if (!networkCapabilities.hasCapability(
                     NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)) {
@@ -3284,7 +3494,7 @@
             }
             onCapabilitiesChanged(network, networkCapabilities);
             onLinkPropertiesChanged(network, linkProperties);
-            onBlockedStatusChanged(network, blocked);
+            // No call to onBlockedStatusChanged here. That is done by the caller.
         }
 
         /**
@@ -3380,7 +3590,7 @@
          * calling these methods while in a callback may return an outdated or even a null object.
          *
          * @param network The {@link Network} whose capabilities have changed.
-         * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this
+         * @param networkCapabilities The new {@link NetworkCapabilities} for this
          *                            network.
          */
         public void onCapabilitiesChanged(@NonNull Network network,
@@ -3448,7 +3658,28 @@
          */
         public void onBlockedStatusChanged(@NonNull Network network, boolean blocked) {}
 
+        /**
+         * Called when access to the specified network is blocked or unblocked.
+         *
+         * If a NetworkCallback object implements this method,
+         * {@link #onBlockedStatusChanged(Network, boolean)} will not be called.
+         *
+         * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
+         * {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
+         * this callback as this is prone to race conditions : calling these methods while in a
+         * callback may return an outdated or even a null object.
+         *
+         * @param network The {@link Network} whose blocked status has changed.
+         * @param blocked The blocked status of this {@link Network}.
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        public void onBlockedStatusChanged(@NonNull Network network, @BlockedReason int blocked) {
+            onBlockedStatusChanged(network, blocked != 0);
+        }
+
         private NetworkRequest networkRequest;
+        private final int mFlags;
     }
 
     /**
@@ -3473,29 +3704,28 @@
         }
     }
 
-    private static final int BASE = Protocol.BASE_CONNECTIVITY_MANAGER;
     /** @hide */
-    public static final int CALLBACK_PRECHECK            = BASE + 1;
+    public static final int CALLBACK_PRECHECK            = 1;
     /** @hide */
-    public static final int CALLBACK_AVAILABLE           = BASE + 2;
+    public static final int CALLBACK_AVAILABLE           = 2;
     /** @hide arg1 = TTL */
-    public static final int CALLBACK_LOSING              = BASE + 3;
+    public static final int CALLBACK_LOSING              = 3;
     /** @hide */
-    public static final int CALLBACK_LOST                = BASE + 4;
+    public static final int CALLBACK_LOST                = 4;
     /** @hide */
-    public static final int CALLBACK_UNAVAIL             = BASE + 5;
+    public static final int CALLBACK_UNAVAIL             = 5;
     /** @hide */
-    public static final int CALLBACK_CAP_CHANGED         = BASE + 6;
+    public static final int CALLBACK_CAP_CHANGED         = 6;
     /** @hide */
-    public static final int CALLBACK_IP_CHANGED          = BASE + 7;
+    public static final int CALLBACK_IP_CHANGED          = 7;
     /** @hide obj = NetworkCapabilities, arg1 = seq number */
-    private static final int EXPIRE_LEGACY_REQUEST       = BASE + 8;
+    private static final int EXPIRE_LEGACY_REQUEST       = 8;
     /** @hide */
-    public static final int CALLBACK_SUSPENDED           = BASE + 9;
+    public static final int CALLBACK_SUSPENDED           = 9;
     /** @hide */
-    public static final int CALLBACK_RESUMED             = BASE + 10;
+    public static final int CALLBACK_RESUMED             = 10;
     /** @hide */
-    public static final int CALLBACK_BLK_CHANGED         = BASE + 11;
+    public static final int CALLBACK_BLK_CHANGED         = 11;
 
     /** @hide */
     public static String getCallbackName(int whichCallback) {
@@ -3525,7 +3755,7 @@
         }
 
         CallbackHandler(Handler handler) {
-            this(Preconditions.checkNotNull(handler, "Handler cannot be null.").getLooper());
+            this(Objects.requireNonNull(handler, "Handler cannot be null.").getLooper());
         }
 
         @Override
@@ -3562,7 +3792,7 @@
                 case CALLBACK_AVAILABLE: {
                     NetworkCapabilities cap = getObject(message, NetworkCapabilities.class);
                     LinkProperties lp = getObject(message, LinkProperties.class);
-                    callback.onAvailable(network, cap, lp, message.arg1 != 0);
+                    callback.onAvailable(network, cap, lp, message.arg1);
                     break;
                 }
                 case CALLBACK_LOSING: {
@@ -3596,8 +3826,7 @@
                     break;
                 }
                 case CALLBACK_BLK_CHANGED: {
-                    boolean blocked = message.arg1 != 0;
-                    callback.onBlockedStatusChanged(network, blocked);
+                    callback.onBlockedStatusChanged(network, message.arg1);
                 }
             }
         }
@@ -3619,13 +3848,14 @@
     private static final HashMap<NetworkRequest, NetworkCallback> sCallbacks = new HashMap<>();
     private static CallbackHandler sCallbackHandler;
 
-    private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, NetworkCallback callback,
-            int timeoutMs, NetworkRequest.Type reqType, int legacyType, CallbackHandler handler) {
+    private NetworkRequest sendRequestForNetwork(int asUid, NetworkCapabilities need,
+            NetworkCallback callback, int timeoutMs, NetworkRequest.Type reqType, int legacyType,
+            CallbackHandler handler) {
         printStackTrace();
         checkCallbackNotNull(callback);
-        Preconditions.checkArgument(
-                reqType == TRACK_DEFAULT || reqType == TRACK_SYSTEM_DEFAULT || need != null,
-                "null NetworkCapabilities");
+        if (reqType != TRACK_DEFAULT && reqType != TRACK_SYSTEM_DEFAULT && need == null) {
+            throw new IllegalArgumentException("null NetworkCapabilities");
+        }
         final NetworkRequest request;
         final String callingPackageName = mContext.getOpPackageName();
         try {
@@ -3638,14 +3868,15 @@
                 }
                 Messenger messenger = new Messenger(handler);
                 Binder binder = new Binder();
+                final int callbackFlags = callback.mFlags;
                 if (reqType == LISTEN) {
                     request = mService.listenForNetwork(
-                            need, messenger, binder, callingPackageName,
+                            need, messenger, binder, callbackFlags, callingPackageName,
                             getAttributionTag());
                 } else {
                     request = mService.requestNetwork(
-                            need, reqType.ordinal(), messenger, timeoutMs, binder, legacyType,
-                            callingPackageName, getAttributionTag());
+                            asUid, need, reqType.ordinal(), messenger, timeoutMs, binder,
+                            legacyType, callbackFlags, callingPackageName, getAttributionTag());
                 }
                 if (request != null) {
                     sCallbacks.put(request, callback);
@@ -3660,6 +3891,12 @@
         return request;
     }
 
+    private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, NetworkCallback callback,
+            int timeoutMs, NetworkRequest.Type reqType, int legacyType, CallbackHandler handler) {
+        return sendRequestForNetwork(Process.INVALID_UID, need, callback, timeoutMs, reqType,
+                legacyType, handler);
+    }
+
     /**
      * Helper function to request a network with a particular legacy type.
      *
@@ -3692,7 +3929,7 @@
     }
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
+     * Request a network to satisfy a set of {@link NetworkCapabilities}.
      *
      * <p>This method will attempt to find the best network that matches the passed
      * {@link NetworkRequest}, and to bring up one that does if none currently satisfies the
@@ -3776,7 +4013,7 @@
     }
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
+     * Request a network to satisfy a set of {@link NetworkCapabilities}.
      *
      * This method behaves identically to {@link #requestNetwork(NetworkRequest, NetworkCallback)}
      * but runs all the callbacks on the passed Handler.
@@ -3798,7 +4035,7 @@
     }
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
+     * Request a network to satisfy a set of {@link NetworkCapabilities}, limited
      * by a timeout.
      *
      * This function behaves identically to the non-timed-out version
@@ -3833,7 +4070,7 @@
     }
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
+     * Request a network to satisfy a set of {@link NetworkCapabilities}, limited
      * by a timeout.
      *
      * This method behaves identically to
@@ -3878,7 +4115,7 @@
 
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
+     * Request a network to satisfy a set of {@link NetworkCapabilities}.
      *
      * This function behaves identically to the version that takes a NetworkCallback, but instead
      * of {@link NetworkCallback} a {@link PendingIntent} is used.  This means
@@ -3971,15 +4208,17 @@
     }
 
     private static void checkPendingIntentNotNull(PendingIntent intent) {
-        Preconditions.checkNotNull(intent, "PendingIntent cannot be null.");
+        Objects.requireNonNull(intent, "PendingIntent cannot be null.");
     }
 
     private static void checkCallbackNotNull(NetworkCallback callback) {
-        Preconditions.checkNotNull(callback, "null NetworkCallback");
+        Objects.requireNonNull(callback, "null NetworkCallback");
     }
 
     private static void checkTimeout(int timeoutMs) {
-        Preconditions.checkArgumentPositive(timeoutMs, "timeoutMs must be strictly positive.");
+        if (timeoutMs <= 0) {
+            throw new IllegalArgumentException("timeoutMs must be strictly positive.");
+        }
     }
 
     /**
@@ -4143,8 +4382,40 @@
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
     public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback,
             @NonNull Handler handler) {
+        registerDefaultNetworkCallbackAsUid(Process.INVALID_UID, networkCallback, handler);
+    }
+
+    /**
+     * Registers to receive notifications about changes in the default network for the specified
+     * UID. This may be a physical network or a virtual network, such as a VPN that applies to the
+     * UID. The callbacks will continue to be called until either the application exits or
+     * {@link #unregisterNetworkCallback(NetworkCallback)} is called.
+     *
+     * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
+     * number of outstanding requests to 100 per app (identified by their UID), shared with
+     * all variants of this method, of {@link #requestNetwork} as well as
+     * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+     * Requesting a network with this method will count toward this limit. If this limit is
+     * exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources,
+     * make sure to unregister the callbacks with
+     * {@link #unregisterNetworkCallback(NetworkCallback)}.
+     *
+     * @param uid the UID for which to track default network changes.
+     * @param networkCallback The {@link NetworkCallback} that the system will call as the
+     *                        UID's default network changes.
+     * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+     * @throws RuntimeException if the app already has too many callbacks registered.
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @SuppressLint({"ExecutorRegistration", "PairedRegistration"})
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_SETTINGS})
+    public void registerDefaultNetworkCallbackAsUid(int uid,
+            @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
         CallbackHandler cbHandler = new CallbackHandler(handler);
-        sendRequestForNetwork(null /* NetworkCapabilities need */, networkCallback, 0,
+        sendRequestForNetwork(uid, null /* need */, networkCallback, 0 /* timeoutMs */,
                 TRACK_DEFAULT, TYPE_NONE, cbHandler);
     }
 
@@ -4190,6 +4461,36 @@
     }
 
     /**
+     * Registers to receive notifications about the best matching network which satisfy the given
+     * {@link NetworkRequest}.  The callbacks will continue to be called until
+     * either the application exits or {@link #unregisterNetworkCallback(NetworkCallback)} is
+     * called.
+     *
+     * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
+     * number of outstanding requests to 100 per app (identified by their UID), shared with
+     * {@link #registerNetworkCallback} and its variants and {@link #requestNetwork} as well as
+     * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+     * Requesting a network with this method will count toward this limit. If this limit is
+     * exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources,
+     * make sure to unregister the callbacks with
+     * {@link #unregisterNetworkCallback(NetworkCallback)}.
+     *
+     *
+     * @param request {@link NetworkRequest} describing this request.
+     * @param networkCallback The {@link NetworkCallback} that the system will call as suitable
+     *                        networks change state.
+     * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+     * @throws RuntimeException if the app already has too many callbacks registered.
+     */
+    @SuppressLint("ExecutorRegistration")
+    public void registerBestMatchingNetworkCallback(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
+        final NetworkCapabilities nc = request.networkCapabilities;
+        final CallbackHandler cbHandler = new CallbackHandler(handler);
+        sendRequestForNetwork(nc, networkCallback, 0, LISTEN_FOR_BEST, TYPE_NONE, cbHandler);
+    }
+
+    /**
      * Requests bandwidth update for a given {@link Network} and returns whether the update request
      * is accepted by ConnectivityService. Once accepted, ConnectivityService will poll underlying
      * network connection for updated bandwidth information. The caller will be notified via
@@ -4229,8 +4530,9 @@
         // Find all requests associated to this callback and stop callback triggers immediately.
         // Callback is reusable immediately. http://b/20701525, http://b/35921499.
         synchronized (sCallbacks) {
-            Preconditions.checkArgument(networkCallback.networkRequest != null,
-                    "NetworkCallback was not registered");
+            if (networkCallback.networkRequest == null) {
+                throw new IllegalArgumentException("NetworkCallback was not registered");
+            }
             if (networkCallback.networkRequest == ALREADY_UNREGISTERED) {
                 Log.d(TAG, "NetworkCallback was already unregistered");
                 return;
@@ -4281,8 +4583,13 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
-    public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD,
+            android.Manifest.permission.NETWORK_STACK,
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK})
+    public void setAcceptUnvalidated(@NonNull Network network, boolean accept, boolean always) {
         try {
             mService.setAcceptUnvalidated(network, accept, always);
         } catch (RemoteException e) {
@@ -4304,8 +4611,14 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
-    public void setAcceptPartialConnectivity(Network network, boolean accept, boolean always) {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD,
+            android.Manifest.permission.NETWORK_STACK,
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK})
+    public void setAcceptPartialConnectivity(@NonNull Network network, boolean accept,
+            boolean always) {
         try {
             mService.setAcceptPartialConnectivity(network, accept, always);
         } catch (RemoteException e) {
@@ -4323,8 +4636,13 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
-    public void setAvoidUnvalidated(Network network) {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD,
+            android.Manifest.permission.NETWORK_STACK,
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK})
+    public void setAvoidUnvalidated(@NonNull Network network) {
         try {
             mService.setAvoidUnvalidated(network);
         } catch (RemoteException e) {
@@ -4335,12 +4653,20 @@
     /**
      * Requests that the system open the captive portal app on the specified network.
      *
+     * <p>This is to be used on networks where a captive portal was detected, as per
+     * {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL}.
+     *
      * @param network The network to log into.
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
-    public void startCaptivePortalApp(Network network) {
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_STACK,
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
+    })
+    public void startCaptivePortalApp(@NonNull Network network) {
         try {
             mService.startCaptivePortalApp(network);
         } catch (RemoteException e) {
@@ -4454,7 +4780,10 @@
      * Resets all connectivity manager settings back to factory defaults.
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    @SystemApi(client = MODULE_LIBRARIES)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK})
     public void factoryReset() {
         try {
             mService.factoryReset();
@@ -4532,7 +4861,7 @@
                 Log.e(TAG, "Can't set proxy properties", e);
             }
             // Must flush DNS cache as new network may have different DNS resolutions.
-            InetAddress.clearDnsCache();
+            InetAddressCompat.clearDnsCache();
             // Must flush socket pool as idle sockets will be bound to previous network and may
             // cause subsequent fetches to be performed on old network.
             NetworkEventDispatcher.getInstance().onNetworkConfigurationChanged();
@@ -4657,17 +4986,6 @@
     public @interface RestrictBackgroundStatus {
     }
 
-    private INetworkPolicyManager getNetworkPolicyManager() {
-        synchronized (this) {
-            if (mNPManager != null) {
-                return mNPManager;
-            }
-            mNPManager = INetworkPolicyManager.Stub.asInterface(ServiceManager
-                    .getService(Context.NETWORK_POLICY_SERVICE));
-            return mNPManager;
-        }
-    }
-
     /**
      * Determines if the calling application is subject to metered network restrictions while
      * running on background.
@@ -4678,7 +4996,7 @@
      */
     public @RestrictBackgroundStatus int getRestrictBackgroundStatus() {
         try {
-            return getNetworkPolicyManager().getRestrictBackgroundByCaller();
+            return mService.getRestrictBackgroundStatusByCaller();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4808,20 +5126,20 @@
      * {@link QosCallback#onError(QosCallbackException)}.  see: {@link QosCallbackException}.
      *
      * @param socketInfo the socket information used to match QoS events
-     * @param callback receives qos events that satisfy socketInfo
      * @param executor The executor on which the callback will be invoked. The provided
      *                 {@link Executor} must run callback sequentially, otherwise the order of
-     *                 callbacks cannot be guaranteed.
+     *                 callbacks cannot be guaranteed.onQosCallbackRegistered
+     * @param callback receives qos events that satisfy socketInfo
      *
      * @hide
      */
     @SystemApi
     public void registerQosCallback(@NonNull final QosSocketInfo socketInfo,
-            @NonNull final QosCallback callback,
-            @CallbackExecutor @NonNull final Executor executor) {
+            @CallbackExecutor @NonNull final Executor executor,
+            @NonNull final QosCallback callback) {
         Objects.requireNonNull(socketInfo, "socketInfo must be non-null");
-        Objects.requireNonNull(callback, "callback must be non-null");
         Objects.requireNonNull(executor, "executor must be non-null");
+        Objects.requireNonNull(callback, "callback must be non-null");
 
         try {
             synchronized (mQosCallbackConnections) {
@@ -4898,7 +5216,7 @@
     }
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, but
+     * Request a network to satisfy a set of {@link NetworkCapabilities}, but
      * does not cause any networks to retain the NET_CAPABILITY_FOREGROUND capability. This can
      * be used to request that the system provide a network without causing the network to be
      * in the foreground.
@@ -4979,23 +5297,10 @@
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
     })
     public void requestBackgroundNetwork(@NonNull NetworkRequest request,
-            @Nullable Handler handler, @NonNull NetworkCallback networkCallback) {
+            @NonNull Handler handler, @NonNull NetworkCallback networkCallback) {
         final NetworkCapabilities nc = request.networkCapabilities;
         sendRequestForNetwork(nc, networkCallback, 0, BACKGROUND_REQUEST,
-                TYPE_NONE, handler == null ? getDefaultHandler() : new CallbackHandler(handler));
-    }
-
-    /**
-     * Listener for {@link #setOemNetworkPreference(OemNetworkPreferences, Executor,
-     * OnSetOemNetworkPreferenceListener)}.
-     * @hide
-     */
-    @SystemApi
-    public interface OnSetOemNetworkPreferenceListener {
-        /**
-         * Called when setOemNetworkPreference() successfully completes.
-         */
-        void onComplete();
+                TYPE_NONE, new CallbackHandler(handler));
     }
 
     /**
@@ -5020,16 +5325,16 @@
     @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE)
     public void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference,
             @Nullable @CallbackExecutor final Executor executor,
-            @Nullable final OnSetOemNetworkPreferenceListener listener) {
+            @Nullable final Runnable listener) {
         Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
         if (null != listener) {
             Objects.requireNonNull(executor, "Executor must be non-null");
         }
-        final IOnSetOemNetworkPreferenceListener listenerInternal = listener == null ? null :
-                new IOnSetOemNetworkPreferenceListener.Stub() {
+        final IOnCompleteListener listenerInternal = listener == null ? null :
+                new IOnCompleteListener.Stub() {
                     @Override
                     public void onComplete() {
-                        executor.execute(listener::onComplete);
+                        executor.execute(listener::run);
                     }
         };
 
@@ -5040,4 +5345,107 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Request that a user profile is put by default on a network matching a given preference.
+     *
+     * See the documentation for the individual preferences for a description of the supported
+     * behaviors.
+     *
+     * @param profile the profile concerned.
+     * @param preference the preference for this profile.
+     * @param executor an executor to execute the listener on. Optional if listener is null.
+     * @param listener an optional listener to listen for completion of the operation.
+     * @throws IllegalArgumentException if {@code profile} is not a valid user profile.
+     * @throws SecurityException if missing the appropriate permissions.
+     * @hide
+     */
+    // This function is for establishing per-profile default networking and can only be called by
+    // the device policy manager, running as the system server. It would make no sense to call it
+    // on a context for a user because it does not establish a setting on behalf of a user, rather
+    // it establishes a setting for a user on behalf of the DPM.
+    @SuppressLint({"UserHandle"})
+    @SystemApi(client = MODULE_LIBRARIES)
+    @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+    public void setProfileNetworkPreference(@NonNull final UserHandle profile,
+            @ProfileNetworkPreference final int preference,
+            @Nullable @CallbackExecutor final Executor executor,
+            @Nullable final Runnable listener) {
+        if (null != listener) {
+            Objects.requireNonNull(executor, "Pass a non-null executor, or a null listener");
+        }
+        final IOnCompleteListener proxy;
+        if (null == listener) {
+            proxy = null;
+        } else {
+            proxy = new IOnCompleteListener.Stub() {
+                @Override
+                public void onComplete() {
+                    executor.execute(listener::run);
+                }
+            };
+        }
+        try {
+            mService.setProfileNetworkPreference(profile, preference, proxy);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    // The first network ID of IPSec tunnel interface.
+    private static final int TUN_INTF_NETID_START = 0xFC00; // 0xFC00 = 64512
+    // The network ID range of IPSec tunnel interface.
+    private static final int TUN_INTF_NETID_RANGE = 0x0400; // 0x0400 = 1024
+
+    /**
+     * Get the network ID range reserved for IPSec tunnel interfaces.
+     *
+     * @return A Range which indicates the network ID range of IPSec tunnel interface.
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @NonNull
+    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/ConnectivityResources.java b/packages/Connectivity/framework/src/android/net/ConnectivityResources.java
new file mode 100644
index 0000000..18f0de0
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityResources.java
@@ -0,0 +1,108 @@
+/*
+ * 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 static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+
+/**
+ * Utility to obtain the {@link com.android.server.ConnectivityService} {@link Resources}, in the
+ * ServiceConnectivityResources APK.
+ * @hide
+ */
+public class ConnectivityResources {
+    private static final String RESOURCES_APK_INTENT =
+            "com.android.server.connectivity.intent.action.SERVICE_CONNECTIVITY_RESOURCES_APK";
+    private static final String RES_PKG_DIR = "/apex/com.android.tethering/";
+
+    @NonNull
+    private final Context mContext;
+
+    @Nullable
+    private Context mResourcesContext = null;
+
+    @Nullable
+    private static Context sTestResourcesContext = null;
+
+    public ConnectivityResources(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Convenience method to mock all resources for the duration of a test.
+     *
+     * Call with a null context to reset after the test.
+     */
+    @VisibleForTesting
+    public static void setResourcesContextForTest(@Nullable Context testContext) {
+        sTestResourcesContext = testContext;
+    }
+
+    /**
+     * Get the {@link Context} of the resources package.
+     */
+    public synchronized Context getResourcesContext() {
+        if (sTestResourcesContext != null) {
+            return sTestResourcesContext;
+        }
+
+        if (mResourcesContext != null) {
+            return mResourcesContext;
+        }
+
+        final List<ResolveInfo> pkgs = mContext.getPackageManager()
+                .queryIntentActivities(new Intent(RESOURCES_APK_INTENT), MATCH_SYSTEM_ONLY);
+        pkgs.removeIf(pkg -> !pkg.activityInfo.applicationInfo.sourceDir.startsWith(RES_PKG_DIR));
+        if (pkgs.size() > 1) {
+            Log.wtf(ConnectivityResources.class.getSimpleName(),
+                    "More than one package found: " + pkgs);
+        }
+        if (pkgs.isEmpty()) {
+            throw new IllegalStateException("No connectivity resource package found");
+        }
+
+        final Context pkgContext;
+        try {
+            pkgContext = mContext.createPackageContext(
+                    pkgs.get(0).activityInfo.applicationInfo.packageName, 0 /* flags */);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalStateException("Resolved package not found", e);
+        }
+
+        mResourcesContext = pkgContext;
+        return pkgContext;
+    }
+
+    /**
+     * Get the {@link Resources} of the ServiceConnectivityResources APK.
+     */
+    public Resources get() {
+        return getResourcesContext().getResources();
+    }
+}
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java
new file mode 100644
index 0000000..9a00055
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java
@@ -0,0 +1,929 @@
+/*
+ * 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 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.Context;
+import android.net.ConnectivityManager.MultipathPreference;
+import android.net.ConnectivityManager.PrivateDnsMode;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Range;
+
+import com.android.net.module.util.ProxyUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Duration;
+import java.util.List;
+
+/**
+ * A manager class for connectivity module settings.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public class ConnectivitySettingsManager {
+
+    private ConnectivitySettingsManager() {}
+
+    /** Data activity timeout settings */
+
+    /**
+     * Inactivity timeout to track mobile data activity.
+     *
+     * If set to a positive integer, it indicates the inactivity timeout value in seconds to
+     * infer the data activity of mobile network. After a period of no activity on mobile
+     * networks with length specified by the timeout, an {@code ACTION_DATA_ACTIVITY_CHANGE}
+     * intent is fired to indicate a transition of network status from "active" to "idle". Any
+     * subsequent activity on mobile networks triggers the firing of {@code
+     * ACTION_DATA_ACTIVITY_CHANGE} intent indicating transition from "idle" to "active".
+     *
+     * Network activity refers to transmitting or receiving data on the network interfaces.
+     *
+     * Tracking is disabled if set to zero or negative value.
+     *
+     * @hide
+     */
+    public static final String DATA_ACTIVITY_TIMEOUT_MOBILE = "data_activity_timeout_mobile";
+
+    /**
+     * Timeout to tracking Wifi data activity. Same as {@code DATA_ACTIVITY_TIMEOUT_MOBILE}
+     * but for Wifi network.
+     *
+     * @hide
+     */
+    public static final String DATA_ACTIVITY_TIMEOUT_WIFI = "data_activity_timeout_wifi";
+
+    /** Dns resolver settings */
+
+    /**
+     * Sample validity in seconds to configure for the system DNS resolver.
+     *
+     * @hide
+     */
+    public static final String DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS =
+            "dns_resolver_sample_validity_seconds";
+
+    /**
+     * Success threshold in percent for use with the system DNS resolver.
+     *
+     * @hide
+     */
+    public static final String DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT =
+            "dns_resolver_success_threshold_percent";
+
+    /**
+     * Minimum number of samples needed for statistics to be considered meaningful in the
+     * system DNS resolver.
+     *
+     * @hide
+     */
+    public static final String DNS_RESOLVER_MIN_SAMPLES = "dns_resolver_min_samples";
+
+    /**
+     * Maximum number taken into account for statistics purposes in the system DNS resolver.
+     *
+     * @hide
+     */
+    public static final String DNS_RESOLVER_MAX_SAMPLES = "dns_resolver_max_samples";
+
+    private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
+    private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
+
+    /** Network switch notification settings */
+
+    /**
+     * The maximum number of notifications shown in 24 hours when switching networks.
+     *
+     * @hide
+     */
+    public static final String NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT =
+            "network_switch_notification_daily_limit";
+
+    /**
+     * The minimum time in milliseconds between notifications when switching networks.
+     *
+     * @hide
+     */
+    public static final String NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS =
+            "network_switch_notification_rate_limit_millis";
+
+    /** Captive portal settings */
+
+    /**
+     * The URL used for HTTP captive portal detection upon a new connection.
+     * A 204 response code from the server is used for validation.
+     *
+     * @hide
+     */
+    public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url";
+
+    /**
+     * What to do when connecting a network that presents a captive portal.
+     * Must be one of the CAPTIVE_PORTAL_MODE_* constants below.
+     *
+     * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT.
+     *
+     * @hide
+     */
+    public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode";
+
+    /**
+     * Don't attempt to detect captive portals.
+     */
+    public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0;
+
+    /**
+     * When detecting a captive portal, display a notification that
+     * prompts the user to sign in.
+     */
+    public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1;
+
+    /**
+     * When detecting a captive portal, immediately disconnect from the
+     * network and do not reconnect to that network in the future.
+     */
+    public static final int CAPTIVE_PORTAL_MODE_AVOID = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            CAPTIVE_PORTAL_MODE_IGNORE,
+            CAPTIVE_PORTAL_MODE_PROMPT,
+            CAPTIVE_PORTAL_MODE_AVOID,
+    })
+    public @interface CaptivePortalMode {}
+
+    /** Global http proxy settings */
+
+    /**
+     * Host name for global http proxy. Set via ConnectivityManager.
+     *
+     * @hide
+     */
+    public static final String GLOBAL_HTTP_PROXY_HOST = "global_http_proxy_host";
+
+    /**
+     * Integer host port for global http proxy. Set via ConnectivityManager.
+     *
+     * @hide
+     */
+    public static final String GLOBAL_HTTP_PROXY_PORT = "global_http_proxy_port";
+
+    /**
+     * Exclusion list for global proxy. This string contains a list of
+     * comma-separated domains where the global proxy does not apply.
+     * Domains should be listed in a comma- separated list. Example of
+     * acceptable formats: ".domain1.com,my.domain2.com" Use
+     * ConnectivityManager to set/get.
+     *
+     * @hide
+     */
+    public static final String GLOBAL_HTTP_PROXY_EXCLUSION_LIST =
+            "global_http_proxy_exclusion_list";
+
+    /**
+     * The location PAC File for the proxy.
+     *
+     * @hide
+     */
+    public static final String GLOBAL_HTTP_PROXY_PAC = "global_proxy_pac_url";
+
+    /** Private dns settings */
+
+    /**
+     * The requested Private DNS mode (string), and an accompanying specifier (string).
+     *
+     * Currently, the specifier holds the chosen provider name when the mode requests
+     * a specific provider. It may be used to store the provider name even when the
+     * mode changes so that temporarily disabling and re-enabling the specific
+     * provider mode does not necessitate retyping the provider hostname.
+     *
+     * @hide
+     */
+    public static final String PRIVATE_DNS_MODE = "private_dns_mode";
+
+    /**
+     * The specific Private DNS provider name.
+     *
+     * @hide
+     */
+    public static final String PRIVATE_DNS_SPECIFIER = "private_dns_specifier";
+
+    /**
+     * Forced override of the default mode (hardcoded as "automatic", nee "opportunistic").
+     * This allows changing the default mode without effectively disabling other modes,
+     * all of which require explicit user action to enable/configure. See also b/79719289.
+     *
+     * Value is a string, suitable for assignment to PRIVATE_DNS_MODE above.
+     *
+     * @hide
+     */
+    public static final String PRIVATE_DNS_DEFAULT_MODE = "private_dns_default_mode";
+
+    /** Other settings */
+
+    /**
+     * The number of milliseconds to hold on to a PendingIntent based request. This delay gives
+     * the receivers of the PendingIntent an opportunity to make a new network request before
+     * the Network satisfying the request is potentially removed.
+     *
+     * @hide
+     */
+    public static final String CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS =
+            "connectivity_release_pending_intent_delay_ms";
+
+    /**
+     * Whether the mobile data connection should remain active even when higher
+     * priority networks like WiFi are active, to help make network switching faster.
+     *
+     * See ConnectivityService for more info.
+     *
+     * (0 = disabled, 1 = enabled)
+     *
+     * @hide
+     */
+    public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on";
+
+    /**
+     * Whether the wifi data connection should remain active even when higher
+     * priority networks like Ethernet are active, to keep both networks.
+     * In the case where higher priority networks are connected, wifi will be
+     * unused unless an application explicitly requests to use it.
+     *
+     * See ConnectivityService for more info.
+     *
+     * (0 = disabled, 1 = enabled)
+     *
+     * @hide
+     */
+    public static final String WIFI_ALWAYS_REQUESTED = "wifi_always_requested";
+
+    /**
+     * Whether to automatically switch away from wifi networks that lose Internet access.
+     * Only meaningful if config_networkAvoidBadWifi is set to 0, otherwise the system always
+     * avoids such networks. Valid values are:
+     *
+     * 0: Don't avoid bad wifi, don't prompt the user. Get stuck on bad wifi like it's 2013.
+     * null: Ask the user whether to switch away from bad wifi.
+     * 1: Avoid bad wifi.
+     *
+     * @hide
+     */
+    public static final String NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi";
+
+    /**
+     * Don't avoid bad wifi, don't prompt the user. Get stuck on bad wifi like it's 2013.
+     */
+    public static final int NETWORK_AVOID_BAD_WIFI_IGNORE = 0;
+
+    /**
+     * Ask the user whether to switch away from bad wifi.
+     */
+    public static final int NETWORK_AVOID_BAD_WIFI_PROMPT = 1;
+
+    /**
+     * Avoid bad wifi.
+     */
+    public static final int NETWORK_AVOID_BAD_WIFI_AVOID = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            NETWORK_AVOID_BAD_WIFI_IGNORE,
+            NETWORK_AVOID_BAD_WIFI_PROMPT,
+            NETWORK_AVOID_BAD_WIFI_AVOID,
+    })
+    public @interface NetworkAvoidBadWifi {}
+
+    /**
+     * User setting for ConnectivityManager.getMeteredMultipathPreference(). This value may be
+     * overridden by the system based on device or application state. If null, the value
+     * specified by config_networkMeteredMultipathPreference is used.
+     *
+     * @hide
+     */
+    public static final String NETWORK_METERED_MULTIPATH_PREFERENCE =
+            "network_metered_multipath_preference";
+
+    /**
+     * A list of apps 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";
+
+    /**
+     * Get mobile data activity timeout from {@link Settings}.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @param def The default timeout if no setting value.
+     * @return The {@link Duration} of timeout to track mobile data activity.
+     */
+    @NonNull
+    public static Duration getMobileDataActivityTimeout(@NonNull Context context,
+            @NonNull Duration def) {
+        final int timeout = Settings.Global.getInt(
+                context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_MOBILE, (int) def.getSeconds());
+        return Duration.ofSeconds(timeout);
+    }
+
+    /**
+     * Set mobile data activity timeout to {@link Settings}.
+     * Tracking is disabled if set to zero or negative value.
+     *
+     * Note: Only use the number of seconds in this duration, lower second(nanoseconds) will be
+     * ignored.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param timeout The mobile data activity timeout.
+     */
+    public static void setMobileDataActivityTimeout(@NonNull Context context,
+            @NonNull Duration timeout) {
+        Settings.Global.putInt(context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_MOBILE,
+                (int) timeout.getSeconds());
+    }
+
+    /**
+     * Get wifi data activity timeout from {@link Settings}.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @param def The default timeout if no setting value.
+     * @return The {@link Duration} of timeout to track wifi data activity.
+     */
+    @NonNull
+    public static Duration getWifiDataActivityTimeout(@NonNull Context context,
+            @NonNull Duration def) {
+        final int timeout = Settings.Global.getInt(
+                context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_WIFI, (int) def.getSeconds());
+        return Duration.ofSeconds(timeout);
+    }
+
+    /**
+     * Set wifi data activity timeout to {@link Settings}.
+     * Tracking is disabled if set to zero or negative value.
+     *
+     * Note: Only use the number of seconds in this duration, lower second(nanoseconds) will be
+     * ignored.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param timeout The wifi data activity timeout.
+     */
+    public static void setWifiDataActivityTimeout(@NonNull Context context,
+            @NonNull Duration timeout) {
+        Settings.Global.putInt(context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_WIFI,
+                (int) timeout.getSeconds());
+    }
+
+    /**
+     * Get dns resolver sample validity duration from {@link Settings}.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @param def The default duration if no setting value.
+     * @return The {@link Duration} of sample validity duration to configure for the system DNS
+     *         resolver.
+     */
+    @NonNull
+    public static Duration getDnsResolverSampleValidityDuration(@NonNull Context context,
+            @NonNull Duration def) {
+        final int duration = Settings.Global.getInt(context.getContentResolver(),
+                DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS, (int) def.getSeconds());
+        return Duration.ofSeconds(duration);
+    }
+
+    /**
+     * Set dns resolver sample validity duration to {@link Settings}. The duration must be a
+     * positive number of seconds.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param duration The sample validity duration.
+     */
+    public static void setDnsResolverSampleValidityDuration(@NonNull Context context,
+            @NonNull Duration duration) {
+        final int time = (int) duration.getSeconds();
+        if (time <= 0) {
+            throw new IllegalArgumentException("Invalid duration");
+        }
+        Settings.Global.putInt(
+                context.getContentResolver(), DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS, time);
+    }
+
+    /**
+     * Get dns resolver success threshold percent from {@link Settings}.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @param def The default value if no setting value.
+     * @return The success threshold in percent for use with the system DNS resolver.
+     */
+    public static int getDnsResolverSuccessThresholdPercent(@NonNull Context context, int def) {
+        return Settings.Global.getInt(
+                context.getContentResolver(), DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT, def);
+    }
+
+    /**
+     * Set dns resolver success threshold percent to {@link Settings}. The threshold percent must
+     * be 0~100.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param percent The success threshold percent.
+     */
+    public static void setDnsResolverSuccessThresholdPercent(@NonNull Context context,
+            @IntRange(from = 0, to = 100) int percent) {
+        if (percent < 0 || percent > 100) {
+            throw new IllegalArgumentException("Percent must be 0~100");
+        }
+        Settings.Global.putInt(
+                context.getContentResolver(), DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT, percent);
+    }
+
+    /**
+     * Get dns resolver samples range from {@link Settings}.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @return The {@link Range<Integer>} of samples needed for statistics to be considered
+     *         meaningful in the system DNS resolver.
+     */
+    @NonNull
+    public static Range<Integer> getDnsResolverSampleRanges(@NonNull Context context) {
+        final int minSamples = Settings.Global.getInt(context.getContentResolver(),
+                DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES);
+        final int maxSamples = Settings.Global.getInt(context.getContentResolver(),
+                DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES);
+        return new Range<>(minSamples, maxSamples);
+    }
+
+    /**
+     * Set dns resolver samples range to {@link Settings}.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param range The samples range. The minimum number should be more than 0 and the maximum
+     *              number should be less that 64.
+     */
+    public static void setDnsResolverSampleRanges(@NonNull Context context,
+            @NonNull Range<Integer> range) {
+        if (range.getLower() < 0 || range.getUpper() > 64) {
+            throw new IllegalArgumentException("Argument must be 0~64");
+        }
+        Settings.Global.putInt(
+                context.getContentResolver(), DNS_RESOLVER_MIN_SAMPLES, range.getLower());
+        Settings.Global.putInt(
+                context.getContentResolver(), DNS_RESOLVER_MAX_SAMPLES, range.getUpper());
+    }
+
+    /**
+     * Get maximum count (from {@link Settings}) of switching network notifications shown in 24
+     * hours.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @param def The default value if no setting value.
+     * @return The maximum count of notifications shown in 24 hours when switching networks.
+     */
+    public static int getNetworkSwitchNotificationMaximumDailyCount(@NonNull Context context,
+            int def) {
+        return Settings.Global.getInt(
+                context.getContentResolver(), NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT, def);
+    }
+
+    /**
+     * Set maximum count (to {@link Settings}) of switching network notifications shown in 24 hours.
+     * The count must be at least 0.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param count The maximum count of switching network notifications shown in 24 hours.
+     */
+    public static void setNetworkSwitchNotificationMaximumDailyCount(@NonNull Context context,
+            @IntRange(from = 0) int count) {
+        if (count < 0) {
+            throw new IllegalArgumentException("Count must be 0~10.");
+        }
+        Settings.Global.putInt(
+                context.getContentResolver(), NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT, count);
+    }
+
+    /**
+     * Get minimum duration (from {@link Settings}) between each switching network notifications.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @param def The default time if no setting value.
+     * @return The minimum duration between notifications when switching networks.
+     */
+    @NonNull
+    public static Duration getNetworkSwitchNotificationRateDuration(@NonNull Context context,
+            @NonNull Duration def) {
+        final int duration = Settings.Global.getInt(context.getContentResolver(),
+                NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS, (int) def.toMillis());
+        return Duration.ofMillis(duration);
+    }
+
+    /**
+     * Set minimum duration (to {@link Settings}) between each switching network notifications.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param duration The minimum duration between notifications when switching networks.
+     */
+    public static void setNetworkSwitchNotificationRateDuration(@NonNull Context context,
+            @NonNull Duration duration) {
+        final int time = (int) duration.toMillis();
+        if (time < 0) {
+            throw new IllegalArgumentException("Invalid duration.");
+        }
+        Settings.Global.putInt(context.getContentResolver(),
+                NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS, time);
+    }
+
+    /**
+     * Get URL (from {@link Settings}) used for HTTP captive portal detection upon a new connection.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @return The URL used for HTTP captive portal detection upon a new connection.
+     */
+    @Nullable
+    public static String getCaptivePortalHttpUrl(@NonNull Context context) {
+        return Settings.Global.getString(context.getContentResolver(), CAPTIVE_PORTAL_HTTP_URL);
+    }
+
+    /**
+     * Set URL (to {@link Settings}) used for HTTP captive portal detection upon a new connection.
+     * This URL should respond with a 204 response to a GET request to indicate no captive portal is
+     * present. And this URL must be HTTP as redirect responses are used to find captive portal
+     * sign-in pages. If the URL set to null or be incorrect, it will result in captive portal
+     * detection failed and lost the connection.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param url The URL used for HTTP captive portal detection upon a new connection.
+     */
+    public static void setCaptivePortalHttpUrl(@NonNull Context context, @Nullable String url) {
+        Settings.Global.putString(context.getContentResolver(), CAPTIVE_PORTAL_HTTP_URL, url);
+    }
+
+    /**
+     * Get mode (from {@link Settings}) when connecting a network that presents a captive portal.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @param def The default mode if no setting value.
+     * @return The mode when connecting a network that presents a captive portal.
+     */
+    @CaptivePortalMode
+    public static int getCaptivePortalMode(@NonNull Context context,
+            @CaptivePortalMode int def) {
+        return Settings.Global.getInt(context.getContentResolver(), CAPTIVE_PORTAL_MODE, def);
+    }
+
+    /**
+     * Set mode (to {@link Settings}) when connecting a network that presents a captive portal.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param mode The mode when connecting a network that presents a captive portal.
+     */
+    public static void setCaptivePortalMode(@NonNull Context context, @CaptivePortalMode int mode) {
+        if (!(mode == CAPTIVE_PORTAL_MODE_IGNORE
+                || mode == CAPTIVE_PORTAL_MODE_PROMPT
+                || mode == CAPTIVE_PORTAL_MODE_AVOID)) {
+            throw new IllegalArgumentException("Invalid captive portal mode");
+        }
+        Settings.Global.putInt(context.getContentResolver(), CAPTIVE_PORTAL_MODE, mode);
+    }
+
+    /**
+     * Get the global HTTP proxy applied to the device, or null if none.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @return The {@link ProxyInfo} which build from global http proxy settings.
+     */
+    @Nullable
+    public static ProxyInfo getGlobalProxy(@NonNull Context context) {
+        final String host = Settings.Global.getString(
+                context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST);
+        final int port = Settings.Global.getInt(
+                context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* def */);
+        final String exclusionList = Settings.Global.getString(
+                context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
+        final String pacFileUrl = Settings.Global.getString(
+                context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC);
+
+        if (TextUtils.isEmpty(host) && TextUtils.isEmpty(pacFileUrl)) {
+            return null; // No global proxy.
+        }
+
+        if (TextUtils.isEmpty(pacFileUrl)) {
+            return ProxyInfo.buildDirectProxy(
+                    host, port, ProxyUtils.exclusionStringAsList(exclusionList));
+        } else {
+            return ProxyInfo.buildPacProxy(Uri.parse(pacFileUrl));
+        }
+    }
+
+    /**
+     * Set global http proxy settings from given {@link ProxyInfo}.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param proxyInfo The {@link ProxyInfo} for global http proxy settings which build from
+     *                    {@link ProxyInfo#buildPacProxy(Uri)} or
+     *                    {@link ProxyInfo#buildDirectProxy(String, int, List)}
+     */
+    public static void setGlobalProxy(@NonNull Context context, @NonNull ProxyInfo proxyInfo) {
+        final String host = proxyInfo.getHost();
+        final int port = proxyInfo.getPort();
+        final String exclusionList = proxyInfo.getExclusionListAsString();
+        final String pacFileUrl = proxyInfo.getPacFileUrl().toString();
+
+        if (TextUtils.isEmpty(pacFileUrl)) {
+            Settings.Global.putString(context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, host);
+            Settings.Global.putInt(context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, port);
+            Settings.Global.putString(
+                    context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, exclusionList);
+            Settings.Global.putString(
+                    context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, "" /* value */);
+        } else {
+            Settings.Global.putString(
+                    context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, pacFileUrl);
+            Settings.Global.putString(
+                    context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, "" /* value */);
+            Settings.Global.putInt(
+                    context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* value */);
+            Settings.Global.putString(
+                    context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, "" /* value */);
+        }
+    }
+
+    /**
+     * Clear all global http proxy settings.
+     *
+     * @param context The {@link Context} to set the setting.
+     */
+    public static void clearGlobalProxy(@NonNull Context context) {
+        Settings.Global.putString(
+                context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, "" /* value */);
+        Settings.Global.putInt(
+                context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* value */);
+        Settings.Global.putString(
+                context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, "" /* value */);
+        Settings.Global.putString(
+                context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, "" /* value */);
+    }
+
+    /**
+     * Get specific private dns provider name from {@link Settings}.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @return The specific private dns provider name, or null if no setting value.
+     */
+    @Nullable
+    public static String getPrivateDnsHostname(@NonNull Context context) {
+        return Settings.Global.getString(context.getContentResolver(), PRIVATE_DNS_SPECIFIER);
+    }
+
+    /**
+     * Set specific private dns provider name to {@link Settings}.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param specifier The specific private dns provider name.
+     */
+    public static void setPrivateDnsHostname(@NonNull Context context,
+            @Nullable String specifier) {
+        Settings.Global.putString(context.getContentResolver(), PRIVATE_DNS_SPECIFIER, specifier);
+    }
+
+    /**
+     * Get default private dns mode from {@link Settings}.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @return The default private dns mode.
+     */
+    @PrivateDnsMode
+    @NonNull
+    public static String getPrivateDnsDefaultMode(@NonNull Context context) {
+        return Settings.Global.getString(context.getContentResolver(), PRIVATE_DNS_DEFAULT_MODE);
+    }
+
+    /**
+     * Set default private dns mode to {@link Settings}.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param mode The default private dns mode. This should be one of the PRIVATE_DNS_MODE_*
+     *             constants.
+     */
+    public static void setPrivateDnsDefaultMode(@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_DEFAULT_MODE, mode);
+    }
+
+    /**
+     * Get duration (from {@link Settings}) to keep a PendingIntent-based request.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @param def The default duration if no setting value.
+     * @return The duration to keep a PendingIntent-based request.
+     */
+    @NonNull
+    public static Duration getConnectivityKeepPendingIntentDuration(@NonNull Context context,
+            @NonNull Duration def) {
+        final int duration = Settings.Secure.getInt(context.getContentResolver(),
+                CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, (int) def.toMillis());
+        return Duration.ofMillis(duration);
+    }
+
+    /**
+     * Set duration (to {@link Settings}) to keep a PendingIntent-based request.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param duration The duration to keep a PendingIntent-based request.
+     */
+    public static void setConnectivityKeepPendingIntentDuration(@NonNull Context context,
+            @NonNull Duration duration) {
+        final int time = (int) duration.toMillis();
+        if (time < 0) {
+            throw new IllegalArgumentException("Invalid duration.");
+        }
+        Settings.Secure.putInt(
+                context.getContentResolver(), CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, time);
+    }
+
+    /**
+     * Read from {@link Settings} whether the mobile data connection should remain active
+     * even when higher priority networks are active.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @param def The default value if no setting value.
+     * @return Whether the mobile data connection should remain active even when higher
+     *         priority networks are active.
+     */
+    public static boolean getMobileDataAlwaysOn(@NonNull Context context, boolean def) {
+        final int enable = Settings.Global.getInt(
+                context.getContentResolver(), MOBILE_DATA_ALWAYS_ON, (def ? 1 : 0));
+        return (enable != 0) ? true : false;
+    }
+
+    /**
+     * Write into {@link Settings} whether the mobile data connection should remain active
+     * even when higher priority networks are active.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param enable Whether the mobile data connection should remain active even when higher
+     *               priority networks are active.
+     */
+    public static void setMobileDataAlwaysOn(@NonNull Context context, boolean enable) {
+        Settings.Global.putInt(
+                context.getContentResolver(), MOBILE_DATA_ALWAYS_ON, (enable ? 1 : 0));
+    }
+
+    /**
+     * Read from {@link Settings} whether the wifi data connection should remain active
+     * even when higher priority networks are active.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @param def The default value if no setting value.
+     * @return Whether the wifi data connection should remain active even when higher
+     *         priority networks are active.
+     */
+    public static boolean getWifiAlwaysRequested(@NonNull Context context, boolean def) {
+        final int enable = Settings.Global.getInt(
+                context.getContentResolver(), WIFI_ALWAYS_REQUESTED, (def ? 1 : 0));
+        return (enable != 0) ? true : false;
+    }
+
+    /**
+     * Write into {@link Settings} whether the wifi data connection should remain active
+     * even when higher priority networks are active.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param enable Whether the wifi data connection should remain active even when higher
+     *               priority networks are active
+     */
+    public static void setWifiAlwaysRequested(@NonNull Context context, boolean enable) {
+        Settings.Global.putInt(
+                context.getContentResolver(), WIFI_ALWAYS_REQUESTED, (enable ? 1 : 0));
+    }
+
+    /**
+     * Get avoid bad wifi setting from {@link Settings}.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @return The setting whether to automatically switch away from wifi networks that lose
+     *         internet access.
+     */
+    @NetworkAvoidBadWifi
+    public static int getNetworkAvoidBadWifi(@NonNull Context context) {
+        final String setting =
+                Settings.Global.getString(context.getContentResolver(), NETWORK_AVOID_BAD_WIFI);
+        if ("0".equals(setting)) {
+            return NETWORK_AVOID_BAD_WIFI_IGNORE;
+        } else if ("1".equals(setting)) {
+            return NETWORK_AVOID_BAD_WIFI_AVOID;
+        } else {
+            return NETWORK_AVOID_BAD_WIFI_PROMPT;
+        }
+    }
+
+    /**
+     * Set avoid bad wifi setting to {@link Settings}.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param value Whether to automatically switch away from wifi networks that lose internet
+     *              access.
+     */
+    public static void setNetworkAvoidBadWifi(@NonNull Context context,
+            @NetworkAvoidBadWifi int value) {
+        final String setting;
+        if (value == NETWORK_AVOID_BAD_WIFI_IGNORE) {
+            setting = "0";
+        } else if (value == NETWORK_AVOID_BAD_WIFI_AVOID) {
+            setting = "1";
+        } else if (value == NETWORK_AVOID_BAD_WIFI_PROMPT) {
+            setting = null;
+        } else {
+            throw new IllegalArgumentException("Invalid avoid bad wifi setting");
+        }
+        Settings.Global.putString(context.getContentResolver(), NETWORK_AVOID_BAD_WIFI, setting);
+    }
+
+    /**
+     * Get network metered multipath preference from {@link Settings}.
+     *
+     * @param context The {@link Context} to query the setting.
+     * @return The network metered multipath preference which should be one of
+     *         ConnectivityManager#MULTIPATH_PREFERENCE_* value or null if the value specified
+     *         by config_networkMeteredMultipathPreference is used.
+     */
+    @Nullable
+    public static String getNetworkMeteredMultipathPreference(@NonNull Context context) {
+        return Settings.Global.getString(
+                context.getContentResolver(), NETWORK_METERED_MULTIPATH_PREFERENCE);
+    }
+
+    /**
+     * Set network metered multipath preference to {@link Settings}.
+     *
+     * @param context The {@link Context} to set the setting.
+     * @param preference The network metered multipath preference which should be one of
+     *                   ConnectivityManager#MULTIPATH_PREFERENCE_* value or null if the value
+     *                   specified by config_networkMeteredMultipathPreference is used.
+     */
+    public static void setNetworkMeteredMultipathPreference(@NonNull Context context,
+            @NonNull @MultipathPreference String preference) {
+        if (!(Integer.valueOf(preference) == MULTIPATH_PREFERENCE_HANDOVER
+                || Integer.valueOf(preference) == MULTIPATH_PREFERENCE_RELIABILITY
+                || Integer.valueOf(preference) == MULTIPATH_PREFERENCE_PERFORMANCE)) {
+            throw new IllegalArgumentException("Invalid private dns mode");
+        }
+        Settings.Global.putString(
+                context.getContentResolver(), NETWORK_METERED_MULTIPATH_PREFERENCE, preference);
+    }
+
+    /**
+     * Get the list of apps(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
+     *         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);
+    }
+
+    /**
+     * Set the list of apps(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
+     *             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);
+    }
+}
diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index cd49258..0826922 100644
--- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -20,7 +20,8 @@
 import android.net.ConnectionInfo;
 import android.net.ConnectivityDiagnosticsManager;
 import android.net.IConnectivityDiagnosticsCallback;
-import android.net.IOnSetOemNetworkPreferenceListener;
+import android.net.INetworkAgent;
+import android.net.IOnCompleteListener;
 import android.net.INetworkActivityListener;
 import android.net.IQosCallback;
 import android.net.ISocketKeepaliveCallback;
@@ -30,6 +31,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkRequest;
+import android.net.NetworkScore;
 import android.net.NetworkState;
 import android.net.NetworkStateSnapshot;
 import android.net.OemNetworkPreferences;
@@ -42,8 +44,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.ResultReceiver;
-
-import com.android.connectivity.aidl.INetworkAgent;
+import android.os.UserHandle;
 
 /**
  * Interface that answers queries about, and allows changing, the
@@ -138,12 +139,12 @@
     void declareNetworkRequestUnfulfillable(in NetworkRequest request);
 
     Network registerNetworkAgent(in INetworkAgent na, in NetworkInfo ni, in LinkProperties lp,
-            in NetworkCapabilities nc, int score, in NetworkAgentConfig config,
+            in NetworkCapabilities nc, in NetworkScore score, in NetworkAgentConfig config,
             in int factorySerialNumber);
 
-    NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities, int reqType,
+    NetworkRequest requestNetwork(int uid, in NetworkCapabilities networkCapabilities, int reqType,
             in Messenger messenger, int timeoutSec, in IBinder binder, int legacy,
-            String callingPackageName, String callingAttributionTag);
+            int callbackFlags, String callingPackageName, String callingAttributionTag);
 
     NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities,
             in PendingIntent operation, String callingPackageName, String callingAttributionTag);
@@ -151,7 +152,7 @@
     void releasePendingNetworkRequest(in PendingIntent operation);
 
     NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities,
-            in Messenger messenger, in IBinder binder, String callingPackageName,
+            in Messenger messenger, in IBinder binder, int callbackFlags, String callingPackageName,
             String callingAttributionTag);
 
     void pendingListenForNetwork(in NetworkCapabilities networkCapabilities,
@@ -214,5 +215,10 @@
     void unregisterQosCallback(in IQosCallback callback);
 
     void setOemNetworkPreference(in OemNetworkPreferences preference,
-            in IOnSetOemNetworkPreferenceListener listener);
+            in IOnCompleteListener listener);
+
+    void setProfileNetworkPreference(in UserHandle profile, int preference,
+            in IOnCompleteListener listener);
+
+    int getRestrictBackgroundStatusByCaller();
 }
diff --git a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl b/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl
similarity index 93%
rename from packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl
rename to packages/Connectivity/framework/src/android/net/INetworkAgent.aidl
index 64b5567..f9d3994 100644
--- a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl
+++ b/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl
@@ -13,13 +13,13 @@
  * See the License for the specific language governing perNmissions and
  * limitations under the License.
  */
-package com.android.connectivity.aidl;
+package android.net;
 
 import android.net.NattKeepalivePacketData;
 import android.net.QosFilterParcelable;
 import android.net.TcpKeepalivePacketData;
 
-import com.android.connectivity.aidl.INetworkAgentRegistry;
+import android.net.INetworkAgentRegistry;
 
 /**
  * Interface to notify NetworkAgent of connectivity events.
@@ -46,4 +46,6 @@
     void onRemoveKeepalivePacketFilter(int slot);
     void onQosFilterCallbackRegistered(int qosCallbackId, in QosFilterParcelable filterParcel);
     void onQosCallbackUnregistered(int qosCallbackId);
+    void onNetworkCreated();
+    void onNetworkDisconnected();
 }
diff --git a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl b/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl
similarity index 83%
rename from packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl
rename to packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl
index f0193db..cbd6193 100644
--- a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl
+++ b/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl
@@ -13,17 +13,19 @@
  * See the License for the specific language governing perNmissions and
  * limitations under the License.
  */
-package com.android.connectivity.aidl;
+package android.net;
 
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
+import android.net.NetworkScore;
 import android.net.QosSession;
 import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.telephony.data.NrQosSessionAttributes;
 
 /**
- * Interface for NetworkAgents to send network network properties.
+ * Interface for NetworkAgents to send network properties.
  * @hide
  */
 oneway interface INetworkAgentRegistry {
@@ -31,11 +33,12 @@
     void sendLinkProperties(in LinkProperties lp);
     // TODO: consider replacing this by "markConnected()" and removing
     void sendNetworkInfo(in NetworkInfo info);
-    void sendScore(int score);
+    void sendScore(in NetworkScore score);
     void sendExplicitlySelected(boolean explicitlySelected, boolean acceptPartial);
     void sendSocketKeepaliveEvent(int slot, int reason);
     void sendUnderlyingNetworks(in @nullable List<Network> networks);
     void sendEpsQosSessionAvailable(int callbackId, in QosSession session, in EpsBearerQosSessionAttributes attributes);
+    void sendNrQosSessionAvailable(int callbackId, in QosSession session, in NrQosSessionAttributes attributes);
     void sendQosSessionLost(int qosCallbackId, in QosSession session);
     void sendQosCallbackError(int qosCallbackId, int exceptionType);
 }
diff --git a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl b/packages/Connectivity/framework/src/android/net/IOnCompleteListener.aidl
similarity index 92%
rename from packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
rename to packages/Connectivity/framework/src/android/net/IOnCompleteListener.aidl
index 7979afc..4bb89f6c 100644
--- a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
+++ b/packages/Connectivity/framework/src/android/net/IOnCompleteListener.aidl
@@ -18,6 +18,6 @@
 package android.net;
 
 /** @hide */
-oneway interface IOnSetOemNetworkPreferenceListener {
+oneway interface IOnCompleteListener {
     void onComplete();
 }
diff --git a/packages/Connectivity/framework/src/android/net/IQosCallback.aidl b/packages/Connectivity/framework/src/android/net/IQosCallback.aidl
index 91c7575..c973541 100644
--- a/packages/Connectivity/framework/src/android/net/IQosCallback.aidl
+++ b/packages/Connectivity/framework/src/android/net/IQosCallback.aidl
@@ -19,6 +19,7 @@
 import android.os.Bundle;
 import android.net.QosSession;
 import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.telephony.data.NrQosSessionAttributes;
 
 /**
  * AIDL interface for QosCallback
@@ -29,6 +30,8 @@
 {
      void onQosEpsBearerSessionAvailable(in QosSession session,
         in EpsBearerQosSessionAttributes attributes);
+     void onNrQosSessionAvailable(in QosSession session,
+        in NrQosSessionAttributes attributes);
      void onQosSessionLost(in QosSession session);
      void onError(in int type);
 }
diff --git a/packages/Connectivity/framework/src/android/net/InetAddressCompat.java b/packages/Connectivity/framework/src/android/net/InetAddressCompat.java
new file mode 100644
index 0000000..6b7e75c
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/InetAddressCompat.java
@@ -0,0 +1,88 @@
+/*
+ * 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.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * Compatibility utility for InetAddress core platform APIs.
+ *
+ * Connectivity has access to such APIs, but they are not part of the module_current stubs yet
+ * (only core_current). Most stable core platform APIs are included manually in the connectivity
+ * build rules, but because InetAddress is also part of the base java SDK that is earlier on the
+ * classpath, the extra core platform APIs are not seen.
+ *
+ * TODO (b/183097033): remove this utility as soon as core_current is part of module_current
+ * @hide
+ */
+public class InetAddressCompat {
+
+    /**
+     * @see InetAddress#clearDnsCache()
+     */
+    public static void clearDnsCache() {
+        try {
+            InetAddress.class.getMethod("clearDnsCache").invoke(null);
+        } catch (InvocationTargetException e) {
+            if (e.getCause() instanceof RuntimeException) {
+                throw (RuntimeException) e.getCause();
+            }
+            throw new IllegalStateException("Unknown InvocationTargetException", e.getCause());
+        } catch (IllegalAccessException | NoSuchMethodException e) {
+            Log.wtf(InetAddressCompat.class.getSimpleName(), "Error clearing DNS cache", e);
+        }
+    }
+
+    /**
+     * @see InetAddress#getAllByNameOnNet(String, int)
+     */
+    public static InetAddress[] getAllByNameOnNet(String host, int netId) throws
+            UnknownHostException {
+        return (InetAddress[]) callGetByNameMethod("getAllByNameOnNet", host, netId);
+    }
+
+    /**
+     * @see InetAddress#getByNameOnNet(String, int)
+     */
+    public static InetAddress getByNameOnNet(String host, int netId) throws
+            UnknownHostException {
+        return (InetAddress) callGetByNameMethod("getByNameOnNet", host, netId);
+    }
+
+    private static Object callGetByNameMethod(String method, String host, int netId)
+            throws UnknownHostException {
+        try {
+            return InetAddress.class.getMethod(method, String.class, int.class)
+                    .invoke(null, host, netId);
+        } catch (InvocationTargetException e) {
+            if (e.getCause() instanceof UnknownHostException) {
+                throw (UnknownHostException) e.getCause();
+            }
+            if (e.getCause() instanceof RuntimeException) {
+                throw (RuntimeException) e.getCause();
+            }
+            throw new IllegalStateException("Unknown InvocationTargetException", e.getCause());
+        } catch (IllegalAccessException | NoSuchMethodException e) {
+            Log.wtf(InetAddressCompat.class.getSimpleName(), "Error calling " + method, e);
+            throw new IllegalStateException("Error querying via " + method, e);
+        }
+    }
+}
diff --git a/packages/Connectivity/framework/src/android/net/IpPrefix.java b/packages/Connectivity/framework/src/android/net/IpPrefix.java
index d2ee7d1..bf4481a 100644
--- a/packages/Connectivity/framework/src/android/net/IpPrefix.java
+++ b/packages/Connectivity/framework/src/android/net/IpPrefix.java
@@ -113,7 +113,7 @@
         // first statement in constructor". We could factor out setting the member variables to an
         // init() method, but if we did, then we'd have to make the members non-final, or "error:
         // cannot assign a value to final variable address". So we just duplicate the code here.
-        Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(prefix);
+        Pair<InetAddress, Integer> ipAndMask = NetworkUtils.legacyParseIpAndMask(prefix);
         this.address = ipAndMask.first.getAddress();
         this.prefixLength = ipAndMask.second;
         checkAndMaskAddressAndPrefixLength();
diff --git a/packages/Connectivity/framework/src/android/net/LinkAddress.java b/packages/Connectivity/framework/src/android/net/LinkAddress.java
index d1bdaa0..d48b8c7 100644
--- a/packages/Connectivity/framework/src/android/net/LinkAddress.java
+++ b/packages/Connectivity/framework/src/android/net/LinkAddress.java
@@ -325,7 +325,7 @@
     public LinkAddress(@NonNull String address, int flags, int scope) {
         // This may throw an IllegalArgumentException; catching it is the caller's responsibility.
         // TODO: consider rejecting mapped IPv4 addresses such as "::ffff:192.0.2.5/24".
-        Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(address);
+        Pair<InetAddress, Integer> ipAndMask = NetworkUtils.legacyParseIpAndMask(address);
         init(ipAndMask.first, ipAndMask.second, flags, scope, LIFETIME_UNKNOWN, LIFETIME_UNKNOWN);
     }
 
diff --git a/packages/Connectivity/framework/src/android/net/MacAddress.java b/packages/Connectivity/framework/src/android/net/MacAddress.java
index c83c23a..26a504a 100644
--- a/packages/Connectivity/framework/src/android/net/MacAddress.java
+++ b/packages/Connectivity/framework/src/android/net/MacAddress.java
@@ -25,7 +25,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import com.android.internal.util.Preconditions;
 import com.android.net.module.util.MacAddressUtils;
 
 import java.lang.annotation.Retention;
@@ -34,6 +33,7 @@
 import java.net.UnknownHostException;
 import java.security.SecureRandom;
 import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * Representation of a MAC address.
@@ -229,7 +229,7 @@
      * @hide
      */
     public static @NonNull byte[] byteAddrFromStringAddr(String addr) {
-        Preconditions.checkNotNull(addr);
+        Objects.requireNonNull(addr);
         String[] parts = addr.split(":");
         if (parts.length != ETHER_ADDR_LEN) {
             throw new IllegalArgumentException(addr + " was not a valid MAC address");
@@ -275,7 +275,7 @@
     // Internal conversion function equivalent to longAddrFromByteAddr(byteAddrFromStringAddr(addr))
     // that avoids the allocation of an intermediary byte[].
     private static long longAddrFromStringAddr(String addr) {
-        Preconditions.checkNotNull(addr);
+        Objects.requireNonNull(addr);
         String[] parts = addr.split(":");
         if (parts.length != ETHER_ADDR_LEN) {
             throw new IllegalArgumentException(addr + " was not a valid MAC address");
@@ -364,8 +364,8 @@
      *
      */
     public boolean matches(@NonNull MacAddress baseAddress, @NonNull MacAddress mask) {
-        Preconditions.checkNotNull(baseAddress);
-        Preconditions.checkNotNull(mask);
+        Objects.requireNonNull(baseAddress);
+        Objects.requireNonNull(mask);
         return (mAddr & mask.mAddr) == (baseAddress.mAddr & mask.mAddr);
     }
 
diff --git a/packages/Connectivity/framework/src/android/net/Network.java b/packages/Connectivity/framework/src/android/net/Network.java
index 46141e0..0741414 100644
--- a/packages/Connectivity/framework/src/android/net/Network.java
+++ b/packages/Connectivity/framework/src/android/net/Network.java
@@ -30,10 +30,10 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.okhttp.internalandroidapi.Dns;
-import com.android.okhttp.internalandroidapi.HttpURLConnectionFactory;
 
 import libcore.io.IoUtils;
+import libcore.net.http.Dns;
+import libcore.net.http.HttpURLConnectionFactory;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -142,7 +142,7 @@
      * @throws UnknownHostException if the address lookup fails.
      */
     public InetAddress[] getAllByName(String host) throws UnknownHostException {
-        return InetAddress.getAllByNameOnNet(host, getNetIdForResolv());
+        return InetAddressCompat.getAllByNameOnNet(host, getNetIdForResolv());
     }
 
     /**
@@ -155,7 +155,7 @@
      *             if the address lookup fails.
      */
     public InetAddress getByName(String host) throws UnknownHostException {
-        return InetAddress.getByNameOnNet(host, getNetIdForResolv());
+        return InetAddressCompat.getByNameOnNet(host, getNetIdForResolv());
     }
 
     /**
@@ -299,7 +299,7 @@
         // Set configuration on the HttpURLConnectionFactory that will be good for all
         // connections created by this Network. Configuration that might vary is left
         // until openConnection() and passed as arguments.
-        HttpURLConnectionFactory urlConnectionFactory = new HttpURLConnectionFactory();
+        HttpURLConnectionFactory urlConnectionFactory = HttpURLConnectionFactory.createInstance();
         urlConnectionFactory.setDns(dnsLookup); // Let traffic go via dnsLookup
         // A private connection pool just for this Network.
         urlConnectionFactory.setNewConnectionPool(httpMaxConnections,
diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
index 27aa15d..01b88aa 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkAgent.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
@@ -32,12 +32,10 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.telephony.data.NrQosSessionAttributes;
 import android.util.Log;
 
-import com.android.connectivity.aidl.INetworkAgent;
-import com.android.connectivity.aidl.INetworkAgentRegistry;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Protocol;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -125,7 +123,10 @@
      */
     public final int providerId;
 
-    private static final int BASE = Protocol.BASE_NETWORK_AGENT;
+    // ConnectivityService parses message constants from itself and NetworkAgent with MessageUtils
+    // for debugging purposes, and crashes if some messages have the same values.
+    // TODO: have ConnectivityService store message names in different maps and remove this base
+    private static final int BASE = 200;
 
     /**
      * Sent by ConnectivityService to the NetworkAgent to inform it of
@@ -361,6 +362,22 @@
      */
     public static final int CMD_UNREGISTER_QOS_CALLBACK = BASE + 21;
 
+    /**
+     * Sent by ConnectivityService to {@link NetworkAgent} to inform the agent that its native
+     * network was created and the Network object is now valid.
+     *
+     * @hide
+     */
+    public static final int CMD_NETWORK_CREATED = BASE + 22;
+
+    /**
+     * Sent by ConnectivityService to {@link NetworkAgent} to inform the agent that its native
+     * network was destroyed.
+     *
+     * @hide
+     */
+    public static final int CMD_NETWORK_DISCONNECTED = BASE + 23;
+
     private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
         // The subtype can be changed with (TODO) setLegacySubtype, but it starts
         // with 0 (TelephonyManager.NETWORK_TYPE_UNKNOWN) and an empty description.
@@ -371,6 +388,14 @@
         return ni;
     }
 
+    // Temporary backward compatibility constructor
+    public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag,
+            @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
+            @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) {
+        this(context, looper, logTag, nc, lp,
+                new NetworkScore.Builder().setLegacyInt(score).build(), config, provider);
+    }
+
     /**
      * Create a new network agent.
      * @param context a {@link Context} to get system services from.
@@ -384,8 +409,9 @@
      * @param provider the {@link NetworkProvider} managing this agent.
      */
     public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag,
-            @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
-            @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) {
+            @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp,
+            @NonNull NetworkScore score, @NonNull NetworkAgentConfig config,
+            @Nullable NetworkProvider provider) {
         this(looper, context, logTag, nc, lp, score, config,
                 provider == null ? NetworkProvider.ID_NONE : provider.getProviderId(),
                 getLegacyNetworkInfo(config));
@@ -395,12 +421,12 @@
         public final Context context;
         public final NetworkCapabilities capabilities;
         public final LinkProperties properties;
-        public final int score;
+        public final NetworkScore score;
         public final NetworkAgentConfig config;
         public final NetworkInfo info;
         InitialConfiguration(@NonNull Context context, @NonNull NetworkCapabilities capabilities,
-                @NonNull LinkProperties properties, int score, @NonNull NetworkAgentConfig config,
-                @NonNull NetworkInfo info) {
+                @NonNull LinkProperties properties, @NonNull NetworkScore score,
+                @NonNull NetworkAgentConfig config, @NonNull NetworkInfo info) {
             this.context = context;
             this.capabilities = capabilities;
             this.properties = properties;
@@ -412,8 +438,9 @@
     private volatile InitialConfiguration mInitialConfiguration;
 
     private NetworkAgent(@NonNull Looper looper, @NonNull Context context, @NonNull String logTag,
-            @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
-            @NonNull NetworkAgentConfig config, int providerId, @NonNull NetworkInfo ni) {
+            @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp,
+            @NonNull NetworkScore score, @NonNull NetworkAgentConfig config, int providerId,
+            @NonNull NetworkInfo ni) {
         mHandler = new NetworkAgentHandler(looper);
         LOG_TAG = logTag;
         mNetworkInfo = new NetworkInfo(ni);
@@ -423,7 +450,7 @@
         }
 
         mInitialConfiguration = new InitialConfiguration(context,
-                new NetworkCapabilities(nc, /* parcelLocationSensitiveFields */ true),
+                new NetworkCapabilities(nc, NetworkCapabilities.REDACT_NONE),
                 new LinkProperties(lp), score, config, ni);
     }
 
@@ -551,6 +578,14 @@
                             msg.arg1 /* QoS callback id */);
                     break;
                 }
+                case CMD_NETWORK_CREATED: {
+                    onNetworkCreated();
+                    break;
+                }
+                case CMD_NETWORK_DISCONNECTED: {
+                    onNetworkDisconnected();
+                    break;
+                }
             }
         }
     }
@@ -691,6 +726,16 @@
             mHandler.sendMessage(mHandler.obtainMessage(
                     CMD_UNREGISTER_QOS_CALLBACK, qosCallbackId, 0, null));
         }
+
+        @Override
+        public void onNetworkCreated() {
+            mHandler.sendMessage(mHandler.obtainMessage(CMD_NETWORK_CREATED));
+        }
+
+        @Override
+        public void onNetworkDisconnected() {
+            mHandler.sendMessage(mHandler.obtainMessage(CMD_NETWORK_DISCONNECTED));
+        }
     }
 
     /**
@@ -867,21 +912,29 @@
         mBandwidthUpdatePending.set(false);
         mLastBwRefreshTime = System.currentTimeMillis();
         final NetworkCapabilities nc =
-                new NetworkCapabilities(networkCapabilities,
-                        /* parcelLocationSensitiveFields */ true);
+                new NetworkCapabilities(networkCapabilities, NetworkCapabilities.REDACT_NONE);
         queueOrSendMessage(reg -> reg.sendNetworkCapabilities(nc));
     }
 
     /**
      * Must be called by the agent to update the score of this network.
      *
+     * @param score the new score.
+     * @hide TODO : unhide when impl is complete
+     */
+    public final void sendNetworkScore(@NonNull NetworkScore score) {
+        Objects.requireNonNull(score);
+        queueOrSendMessage(reg -> reg.sendScore(score));
+    }
+
+    /**
+     * Must be called by the agent to update the score of this network.
+     *
      * @param score the new score, between 0 and 99.
+     * deprecated use sendNetworkScore(NetworkScore) TODO : remove in S.
      */
     public final void sendNetworkScore(@IntRange(from = 0, to = 99) int score) {
-        if (score < 0) {
-            throw new IllegalArgumentException("Score must be >= 0");
-        }
-        queueOrSendMessage(reg -> reg.sendScore(score));
+        sendNetworkScore(new NetworkScore.Builder().setLegacyInt(score).build());
     }
 
     /**
@@ -991,6 +1044,17 @@
     }
 
     /**
+     * Called when ConnectivityService has successfully created this NetworkAgent's native network.
+     */
+    public void onNetworkCreated() {}
+
+
+    /**
+     * Called when ConnectivityService has successfully destroy this NetworkAgent's native network.
+     */
+    public void onNetworkDisconnected() {}
+
+    /**
      * Requests that the network hardware send the specified packet at the specified interval.
      *
      * @param slot the hardware slot on which to start the keepalive.
@@ -1141,29 +1205,37 @@
 
 
     /**
-     * Sends the attributes of Eps Bearer Qos Session back to the Application
+     * Sends the attributes of Qos Session back to the Application
      *
      * @param qosCallbackId the callback id that the session belongs to
-     * @param sessionId the unique session id across all Eps Bearer Qos Sessions
-     * @param attributes the attributes of the Eps Qos Session
+     * @param sessionId the unique session id across all Qos Sessions
+     * @param attributes the attributes of the Qos Session
      */
     public final void sendQosSessionAvailable(final int qosCallbackId, final int sessionId,
-            @NonNull final EpsBearerQosSessionAttributes attributes) {
+            @NonNull final QosSessionAttributes attributes) {
         Objects.requireNonNull(attributes, "The attributes must be non-null");
-        queueOrSendMessage(ra -> ra.sendEpsQosSessionAvailable(qosCallbackId,
-                new QosSession(sessionId, QosSession.TYPE_EPS_BEARER),
-                attributes));
+        if (attributes instanceof EpsBearerQosSessionAttributes) {
+            queueOrSendMessage(ra -> ra.sendEpsQosSessionAvailable(qosCallbackId,
+                    new QosSession(sessionId, QosSession.TYPE_EPS_BEARER),
+                    (EpsBearerQosSessionAttributes)attributes));
+        } else if (attributes instanceof NrQosSessionAttributes) {
+            queueOrSendMessage(ra -> ra.sendNrQosSessionAvailable(qosCallbackId,
+                    new QosSession(sessionId, QosSession.TYPE_NR_BEARER),
+                    (NrQosSessionAttributes)attributes));
+        }
     }
 
     /**
-     * Sends event that the Eps Qos Session was lost.
+     * Sends event that the Qos Session was lost.
      *
      * @param qosCallbackId the callback id that the session belongs to
-     * @param sessionId the unique session id across all Eps Bearer Qos Sessions
+     * @param sessionId the unique session id across all Qos Sessions
+     * @param qosSessionType the session type {@code QosSesson#QosSessionType}
      */
-    public final void sendQosSessionLost(final int qosCallbackId, final int sessionId) {
+    public final void sendQosSessionLost(final int qosCallbackId,
+            final int sessionId, final int qosSessionType) {
         queueOrSendMessage(ra -> ra.sendQosSessionLost(qosCallbackId,
-                new QosSession(sessionId, QosSession.TYPE_EPS_BEARER)));
+                new QosSession(sessionId, qosSessionType)));
     }
 
     /**
diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.java b/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.java
index 664c265..0bd2371 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.java
@@ -50,7 +50,8 @@
      * ap in the wifi settings to trigger a connection is explicit.  A 3rd party app asking to
      * connect to a particular access point is also explicit, though this may change in the future
      * as we want apps to use the multinetwork apis.
-     *
+     * TODO : this is a bad name, because it sounds like the user just tapped on the network.
+     * It's not necessarily the case ; auto-reconnection to WiFi has this true for example.
      * @hide
      */
     public boolean explicitlySelected;
@@ -63,6 +64,16 @@
     }
 
     /**
+     * @return whether this VPN connection can be bypassed by the apps.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public boolean isBypassableVpn() {
+        return allowBypass;
+    }
+
+    /**
      * Set if the user desires to use this network even if it is unvalidated. This field has meaning
      * only if {@link explicitlySelected} is true. If it is, this field must also be set to the
      * appropriate value based on previous user choice.
@@ -346,6 +357,19 @@
         }
 
         /**
+         * Sets whether the apps can bypass the VPN connection.
+         *
+         * @return this builder, to facilitate chaining.
+         * @hide
+         */
+        @NonNull
+        @SystemApi(client = MODULE_LIBRARIES)
+        public Builder setBypassableVpn(boolean allowBypass) {
+            mConfig.allowBypass = allowBypass;
+            return this;
+        }
+
+        /**
          * Returns the constructed {@link NetworkAgentConfig} object.
          */
         @NonNull
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index c82cd3b..881fa8c 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -19,22 +19,25 @@
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
 
 import android.annotation.IntDef;
+import android.annotation.LongDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.net.ConnectivityManager.NetworkCallback;
+import android.net.wifi.WifiNetworkSuggestion;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.Process;
 import android.text.TextUtils;
 import android.util.ArraySet;
+import android.util.Range;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.NetworkCapabilitiesUtils;
 
@@ -62,6 +65,68 @@
 public final class NetworkCapabilities implements Parcelable {
     private static final String TAG = "NetworkCapabilities";
 
+    /**
+     * Mechanism to support redaction of fields in NetworkCapabilities that are guarded by specific
+     * app permissions.
+     **/
+    /**
+     * Don't redact any fields since the receiving app holds all the necessary permissions.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final long REDACT_NONE = 0;
+
+    /**
+     * Redact any fields that need {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
+     * permission since the receiving app does not hold this permission or the location toggle
+     * is off.
+     *
+     * @see android.Manifest.permission#ACCESS_FINE_LOCATION
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final long REDACT_FOR_ACCESS_FINE_LOCATION = 1 << 0;
+
+    /**
+     * Redact any fields that need {@link android.Manifest.permission#LOCAL_MAC_ADDRESS}
+     * permission since the receiving app does not hold this permission.
+     *
+     * @see android.Manifest.permission#LOCAL_MAC_ADDRESS
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final long REDACT_FOR_LOCAL_MAC_ADDRESS = 1 << 1;
+
+    /**
+     *
+     * Redact any fields that need {@link android.Manifest.permission#NETWORK_SETTINGS}
+     * permission since the receiving app does not hold this permission.
+     *
+     * @see android.Manifest.permission#NETWORK_SETTINGS
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final long REDACT_FOR_NETWORK_SETTINGS = 1 << 2;
+
+    /**
+     * Redact all fields in this object that require any relevant permission.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final long REDACT_ALL = -1L;
+
+    /** @hide */
+    @LongDef(flag = true, prefix = { "REDACT_" }, value = {
+            REDACT_NONE,
+            REDACT_FOR_ACCESS_FINE_LOCATION,
+            REDACT_FOR_LOCAL_MAC_ADDRESS,
+            REDACT_FOR_NETWORK_SETTINGS,
+            REDACT_ALL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RedactionType {}
+
     // Set to true when private DNS is broken.
     private boolean mPrivateDnsBroken;
 
@@ -76,32 +141,31 @@
     private String mRequestorPackageName;
 
     /**
-     * Indicates whether parceling should preserve fields that are set based on permissions of
-     * the process receiving the {@link NetworkCapabilities}.
+     * Indicates what fields should be redacted from this instance.
      */
-    private final boolean mParcelLocationSensitiveFields;
+    private final @RedactionType long mRedactions;
 
     public NetworkCapabilities() {
-        mParcelLocationSensitiveFields = false;
+        mRedactions = REDACT_ALL;
         clearAll();
         mNetworkCapabilities = DEFAULT_CAPABILITIES;
     }
 
     public NetworkCapabilities(NetworkCapabilities nc) {
-        this(nc, false /* parcelLocationSensitiveFields */);
+        this(nc, REDACT_ALL);
     }
 
     /**
      * Make a copy of NetworkCapabilities.
      *
      * @param nc Original NetworkCapabilities
-     * @param parcelLocationSensitiveFields Whether to parcel location sensitive data or not.
+     * @param redactions bitmask of redactions that needs to be performed on this new instance of
+     *                   {@link NetworkCapabilities}.
      * @hide
      */
-    @SystemApi
-    public NetworkCapabilities(
-            @Nullable NetworkCapabilities nc, boolean parcelLocationSensitiveFields) {
-        mParcelLocationSensitiveFields = parcelLocationSensitiveFields;
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public NetworkCapabilities(@Nullable NetworkCapabilities nc, @RedactionType long redactions) {
+        mRedactions = redactions;
         if (nc != null) {
             set(nc);
         }
@@ -113,11 +177,13 @@
      * @hide
      */
     public void clearAll() {
-        // Ensures that the internal copies maintained by the connectivity stack does not set
-        // this bit.
-        if (mParcelLocationSensitiveFields) {
+        // 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 parcelLocationSensitiveFields is set");
+                    "Cannot clear NetworkCapabilities when mRedactions is set");
         }
         mNetworkCapabilities = mTransportTypes = mUnwantedNetworkCapabilities = 0;
         mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
@@ -131,6 +197,7 @@
         mPrivateDnsBroken = false;
         mRequestorUid = Process.INVALID_UID;
         mRequestorPackageName = null;
+        mSubIds = new ArraySet<>();
     }
 
     /**
@@ -146,12 +213,12 @@
         mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
         mNetworkSpecifier = nc.mNetworkSpecifier;
         if (nc.getTransportInfo() != null) {
-            setTransportInfo(nc.getTransportInfo().makeCopy(mParcelLocationSensitiveFields));
+            setTransportInfo(nc.getTransportInfo().makeCopy(mRedactions));
         } else {
             setTransportInfo(null);
         }
         mSignalStrength = nc.mSignalStrength;
-        setUids(nc.mUids); // Will make the defensive copy
+        mUids = (nc.mUids == null) ? null : new ArraySet<>(nc.mUids);
         setAdministratorUids(nc.getAdministratorUids());
         mOwnerUid = nc.mOwnerUid;
         mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities;
@@ -159,6 +226,7 @@
         mPrivateDnsBroken = nc.mPrivateDnsBroken;
         mRequestorUid = nc.mRequestorUid;
         mRequestorPackageName = nc.mRequestorPackageName;
+        mSubIds = new ArraySet<>(nc.mSubIds);
     }
 
     /**
@@ -571,19 +639,31 @@
     }
 
     /**
-     * Removes (if found) the given capability from this {@code NetworkCapability} instance.
+     * Removes (if found) the given capability from this {@code NetworkCapability}
+     * instance that were added via addCapability(int) or setCapabilities(int[], int[]).
      *
      * @param capability the capability to be removed.
      * @return This NetworkCapabilities instance, to facilitate chaining.
      * @hide
      */
     public @NonNull NetworkCapabilities removeCapability(@NetCapability int capability) {
-        // Note that this method removes capabilities that were added via addCapability(int),
-        // addUnwantedCapability(int) or setCapabilities(int[], int[]).
         checkValidCapability(capability);
         final long mask = ~(1 << capability);
         mNetworkCapabilities &= mask;
-        mUnwantedNetworkCapabilities &= mask;
+        return this;
+    }
+
+    /**
+     * Removes (if found) the given unwanted capability from this {@code NetworkCapability}
+     * instance that were added via addUnwantedCapability(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) {
+        checkValidCapability(capability);
+        mUnwantedNetworkCapabilities &= ~(1 << capability);
         return this;
     }
 
@@ -606,10 +686,8 @@
      * Gets all the capabilities set on this {@code NetworkCapability} instance.
      *
      * @return an array of capability values for this instance.
-     * @hide
      */
-    @UnsupportedAppUsage
-    public @NetCapability int[] getCapabilities() {
+    public @NonNull @NetCapability int[] getCapabilities() {
         return NetworkCapabilitiesUtils.unpackBits(mNetworkCapabilities);
     }
 
@@ -657,6 +735,7 @@
     }
 
     /** @hide */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public boolean hasUnwantedCapability(@NetCapability int capability) {
         return isValidCapability(capability)
                 && ((mUnwantedNetworkCapabilities & (1 << capability)) != 0);
@@ -670,10 +749,16 @@
         return ((mNetworkCapabilities & CONNECTIVITY_MANAGED_CAPABILITIES) != 0);
     }
 
-    /** Note this method may result in having the same capability in wanted and unwanted lists. */
     private void combineNetCapabilities(@NonNull NetworkCapabilities nc) {
-        this.mNetworkCapabilities |= nc.mNetworkCapabilities;
-        this.mUnwantedNetworkCapabilities |= nc.mUnwantedNetworkCapabilities;
+        final long wantedCaps = this.mNetworkCapabilities | nc.mNetworkCapabilities;
+        final long unwantedCaps =
+                this.mUnwantedNetworkCapabilities | nc.mUnwantedNetworkCapabilities;
+        if ((wantedCaps & unwantedCaps) != 0) {
+            throw new IllegalArgumentException(
+                    "Cannot have the same capability in wanted and unwanted lists.");
+        }
+        this.mNetworkCapabilities = wantedCaps;
+        this.mUnwantedNetworkCapabilities = unwantedCaps;
     }
 
     /**
@@ -1048,6 +1133,16 @@
      *
      * Instances of NetworkCapabilities sent to apps without the appropriate permissions will have
      * this field cleared out.
+     *
+     * <p>
+     * This field will only be populated for VPN and wifi network suggestor apps (i.e using
+     * {@link WifiNetworkSuggestion}), and only for the network they own.
+     * In the case of wifi network suggestors apps, this field is also location sensitive, so the
+     * app needs to hold {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission. If the
+     * app targets SDK version greater than or equal to {@link Build.VERSION_CODES#S}, then they
+     * also need to use {@link NetworkCallback#FLAG_INCLUDE_LOCATION_INFO} to get the info in their
+     * callback. The app will be blamed for location access if this field is included.
+     * </p>
      */
     public int getOwnerUid() {
         return mOwnerUid;
@@ -1445,9 +1540,8 @@
      * @hide
      */
     public @NonNull NetworkCapabilities setSingleUid(int uid) {
-        final ArraySet<UidRange> identity = new ArraySet<>(1);
-        identity.add(new UidRange(uid, uid));
-        setUids(identity);
+        mUids = new ArraySet<>(1);
+        mUids.add(new UidRange(uid, uid));
         return this;
     }
 
@@ -1456,22 +1550,34 @@
      * This makes a copy of the set so that callers can't modify it after the call.
      * @hide
      */
-    public @NonNull NetworkCapabilities setUids(Set<UidRange> uids) {
-        if (null == uids) {
-            mUids = null;
-        } else {
-            mUids = new ArraySet<>(uids);
-        }
+    public @NonNull NetworkCapabilities setUids(@Nullable Set<Range<Integer>> uids) {
+        mUids = UidRange.fromIntRanges(uids);
         return this;
     }
 
     /**
      * Get the list of UIDs this network applies to.
      * This returns a copy of the set so that callers can't modify the original object.
+     *
+     * @return the list of UIDs this network applies to. If {@code null}, then the network applies
+     *         to all UIDs.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @SuppressLint("NullableCollection")
+    public @Nullable Set<Range<Integer>> getUids() {
+        return UidRange.toIntRanges(mUids);
+    }
+
+    /**
+     * Get the list of UIDs this network applies to.
+     * This returns a copy of the set so that callers can't modify the original object.
      * @hide
      */
-    public @Nullable Set<UidRange> getUids() {
-        return null == mUids ? null : new ArraySet<>(mUids);
+    public @Nullable Set<UidRange> getUidRanges() {
+        if (mUids == null) return null;
+
+        return new ArraySet<>(mUids);
     }
 
     /**
@@ -1655,6 +1761,7 @@
         combineSSIDs(nc);
         combineRequestor(nc);
         combineAdministratorUids(nc);
+        combineSubIds(nc);
     }
 
     /**
@@ -1674,8 +1781,9 @@
                 && satisfiedBySpecifier(nc)
                 && (onlyImmutable || satisfiedBySignalStrength(nc))
                 && (onlyImmutable || satisfiedByUids(nc))
-                && (onlyImmutable || satisfiedBySSID(nc)))
-                && (onlyImmutable || satisfiedByRequestor(nc));
+                && (onlyImmutable || satisfiedBySSID(nc))
+                && (onlyImmutable || satisfiedByRequestor(nc))
+                && (onlyImmutable || satisfiedBySubIds(nc)));
     }
 
     /**
@@ -1771,7 +1879,8 @@
                 && equalsOwnerUid(that)
                 && equalsPrivateDnsBroken(that)
                 && equalsRequestor(that)
-                && equalsAdministratorUids(that);
+                && equalsAdministratorUids(that)
+                && equalsSubIds(that);
     }
 
     @Override
@@ -1793,7 +1902,8 @@
                 + Objects.hashCode(mPrivateDnsBroken) * 47
                 + Objects.hashCode(mRequestorUid) * 53
                 + Objects.hashCode(mRequestorPackageName) * 59
-                + Arrays.hashCode(mAdministratorUids) * 61;
+                + Arrays.hashCode(mAdministratorUids) * 61
+                + Objects.hashCode(mSubIds) * 67;
     }
 
     @Override
@@ -1827,6 +1937,7 @@
         dest.writeInt(mOwnerUid);
         dest.writeInt(mRequestorUid);
         dest.writeString(mRequestorPackageName);
+        dest.writeIntArray(CollectionUtils.toIntArray(mSubIds));
     }
 
     public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -1850,6 +1961,11 @@
                 netCap.mOwnerUid = in.readInt();
                 netCap.mRequestorUid = in.readInt();
                 netCap.mRequestorPackageName = in.readString();
+                netCap.mSubIds = new ArraySet<>();
+                final int[] subIdInts = Objects.requireNonNull(in.createIntArray());
+                for (int i = 0; i < subIdInts.length; i++) {
+                    netCap.mSubIds.add(subIdInts[i]);
+                }
                 return netCap;
             }
             @Override
@@ -1933,11 +2049,14 @@
             sb.append(" SSID: ").append(mSSID);
         }
 
-
         if (mPrivateDnsBroken) {
             sb.append(" PrivateDnsBroken");
         }
 
+        if (!mSubIds.isEmpty()) {
+            sb.append(" SubscriptionIds: ").append(mSubIds);
+        }
+
         sb.append("]");
         return sb.toString();
     }
@@ -2073,8 +2192,9 @@
     }
 
     private static void checkValidTransportType(@Transport int transport) {
-        Preconditions.checkArgument(
-                isValidTransport(transport), "Invalid TransportType " + transport);
+        if (!isValidTransport(transport)) {
+            throw new IllegalArgumentException("Invalid TransportType " + transport);
+        }
     }
 
     private static boolean isValidCapability(@NetworkCapabilities.NetCapability int capability) {
@@ -2082,8 +2202,9 @@
     }
 
     private static void checkValidCapability(@NetworkCapabilities.NetCapability int capability) {
-        Preconditions.checkArgument(isValidCapability(capability),
-                "NetworkCapability " + capability + "out of range");
+        if (!isValidCapability(capability)) {
+            throw new IllegalArgumentException("NetworkCapability " + capability + "out of range");
+        }
     }
 
     /**
@@ -2251,6 +2372,84 @@
     }
 
     /**
+     * Set of the subscription IDs that identifies the network or request, empty if none.
+     */
+    @NonNull
+    private ArraySet<Integer> mSubIds = new ArraySet<>();
+
+    /**
+     * Sets the subscription ID set that associated to this network or request.
+     *
+     * @hide
+     */
+    @NonNull
+    public NetworkCapabilities setSubIds(@NonNull Set<Integer> subIds) {
+        mSubIds = new ArraySet(Objects.requireNonNull(subIds));
+        return this;
+    }
+
+    /**
+     * Gets the subscription ID set that associated to this network or request.
+     * @return
+     */
+    @NonNull
+    public Set<Integer> getSubIds() {
+        return new ArraySet<>(mSubIds);
+    }
+
+    /**
+     * Tests if the subscription ID set of this network is the same as that of the passed one.
+     */
+    private boolean equalsSubIds(@NonNull NetworkCapabilities nc) {
+        return Objects.equals(mSubIds, nc.mSubIds);
+    }
+
+    /**
+     * Check if the subscription ID set requirements of this object are matched by the passed one.
+     * If specified in the request, the passed one need to have at least one subId and at least
+     * one of them needs to be in the request set.
+     */
+    private boolean satisfiedBySubIds(@NonNull NetworkCapabilities nc) {
+        if (mSubIds.isEmpty()) return true;
+        if (nc.mSubIds.isEmpty()) return false;
+        for (final Integer subId : nc.mSubIds) {
+            if (mSubIds.contains(subId)) return true;
+        }
+        return false;
+    }
+
+    /**
+     * Combine subscription ID set of the capabilities.
+     *
+     * <p>This is only legal if the subscription Ids are equal.
+     *
+     * <p>If both subscription IDs are not equal, they belong to different subscription
+     * (or no subscription). In this case, it would not make sense to add them together.
+     */
+    private void combineSubIds(@NonNull NetworkCapabilities nc) {
+        if (!Objects.equals(mSubIds, nc.mSubIds)) {
+            throw new IllegalStateException("Can't combine two subscription ID sets");
+        }
+    }
+
+    /**
+     * Returns a bitmask of all the applicable redactions (based on the permissions held by the
+     * receiving app) to be performed on this object.
+     *
+     * @return bitmask of redactions applicable on this instance.
+     * @hide
+     */
+    public @RedactionType long getApplicableRedactions() {
+        // Currently, there are no fields redacted in NetworkCapabilities itself, so we just
+        // passthrough the redactions required by the embedded TransportInfo. If this changes
+        // in the future, modify this method.
+        if (mTransportInfo == null) {
+            return NetworkCapabilities.REDACT_NONE;
+        }
+        return mTransportInfo.getApplicableRedactions();
+    }
+
+    /**
      * Builder class for NetworkCapabilities.
      *
      * This class is mainly for for {@link NetworkAgent} instances to use. Many fields in
@@ -2556,6 +2755,33 @@
         }
 
         /**
+         * Set the subscription ID set.
+         *
+         * @param subIds a set that represent the subscription IDs. Empty if clean up.
+         * @return this builder.
+         */
+        @NonNull
+        public Builder setSubIds(@NonNull final Set<Integer> subIds) {
+            mCaps.setSubIds(subIds);
+            return this;
+        }
+
+        /**
+         * Set the list of UIDs this network applies to.
+         *
+         * @param uids the list of UIDs this network applies to, or {@code null} if this network
+         *             applies to all UIDs.
+         * @return this builder
+         * @hide
+         */
+        @NonNull
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        public Builder setUids(@Nullable Set<Range<Integer>> uids) {
+            mCaps.setUids(uids);
+            return this;
+        }
+
+        /**
          * Builds the instance of the capabilities.
          *
          * @return the built instance of NetworkCapabilities.
diff --git a/packages/Connectivity/framework/src/android/net/NetworkInfo.java b/packages/Connectivity/framework/src/android/net/NetworkInfo.java
index d752901..bb23494 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkInfo.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkInfo.java
@@ -21,7 +21,6 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.Annotation.NetworkType;
 import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -164,7 +163,7 @@
      * @param typeName a human-readable string for the network type, or an empty string or null.
      * @param subtypeName a human-readable string for the subtype, or an empty string or null.
      */
-    public NetworkInfo(int type, @NetworkType int subtype,
+    public NetworkInfo(int type, int subtype,
             @Nullable String typeName, @Nullable String subtypeName) {
         if (!ConnectivityManager.isNetworkTypeValid(type)
                 && type != ConnectivityManager.TYPE_NONE) {
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
index b4a651c..bcbc04f7 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -31,10 +31,12 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.net.NetworkCapabilities.NetCapability;
@@ -44,6 +46,7 @@
 import android.os.Parcelable;
 import android.os.Process;
 import android.text.TextUtils;
+import android.util.Range;
 import android.util.proto.ProtoOutputStream;
 
 import java.util.Arrays;
@@ -113,6 +116,10 @@
      *       for the network (if any) that satisfies the default Internet
      *       request.
      *
+     *     - TRACK_BEST, which causes the framework to send callbacks about
+     *       the single, highest scoring current network (if any) that matches
+     *       the specified NetworkCapabilities.
+     *
      *     - BACKGROUND_REQUEST, like REQUEST but does not cause any networks
      *       to retain the NET_CAPABILITY_FOREGROUND capability. A network with
      *       no foreground requests is in the background. A network that has
@@ -135,6 +142,7 @@
         REQUEST,
         BACKGROUND_REQUEST,
         TRACK_SYSTEM_DEFAULT,
+        LISTEN_FOR_BEST,
     };
 
     /**
@@ -208,6 +216,14 @@
         }
 
         /**
+         * Creates a new Builder of NetworkRequest from an existing instance.
+         */
+        public Builder(@NonNull final NetworkRequest request) {
+            Objects.requireNonNull(request);
+            mNetworkCapabilities = request.networkCapabilities;
+        }
+
+        /**
          * Build {@link NetworkRequest} give the current set of capabilities.
          */
         public NetworkRequest build() {
@@ -271,11 +287,14 @@
          * Set the watched UIDs for this request. This will be reset and wiped out unless
          * the calling app holds the CHANGE_NETWORK_STATE permission.
          *
-         * @param uids The watched UIDs as a set of UidRanges, or null for everything.
+         * @param uids The watched UIDs as a set of {@code Range<Integer>}, or null for everything.
          * @return The builder to facilitate chaining.
          * @hide
          */
-        public Builder setUids(Set<UidRange> uids) {
+        @NonNull
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public Builder setUids(@Nullable Set<Range<Integer>> uids) {
             mNetworkCapabilities.setUids(uids);
             return this;
         }
@@ -294,12 +313,31 @@
          *
          * @hide
          */
+        @NonNull
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
         public Builder addUnwantedCapability(@NetworkCapabilities.NetCapability int capability) {
             mNetworkCapabilities.addUnwantedCapability(capability);
             return this;
         }
 
         /**
+         * Removes (if found) the given unwanted capability from this builder instance.
+         *
+         * @param capability The unwanted capability to remove.
+         * @return The builder to facilitate chaining.
+         *
+         * @hide
+         */
+        @NonNull
+        @SuppressLint("BuilderSetStyle")
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        public Builder removeUnwantedCapability(@NetworkCapabilities.NetCapability int capability) {
+            mNetworkCapabilities.removeUnwantedCapability(capability);
+            return this;
+        }
+
+        /**
          * Completely clears all the {@code NetworkCapabilities} from this builder instance,
          * removing even the capabilities that are set by default when the object is constructed.
          *
@@ -382,11 +420,17 @@
                 return setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
                         .setSubscriptionId(subId).build());
             } catch (NumberFormatException nfe) {
-                // A StringNetworkSpecifier does not accept null or empty ("") strings. When network
-                // specifiers were strings a null string and an empty string were considered
-                // equivalent. Hence no meaning is attached to a null or empty ("") string.
-                return setNetworkSpecifier(TextUtils.isEmpty(networkSpecifier) ? null
-                        : new StringNetworkSpecifier(networkSpecifier));
+                // An EthernetNetworkSpecifier or TestNetworkSpecifier does not accept null or empty
+                // ("") strings. When network specifiers were strings a null string and an empty
+                // string were considered equivalent. Hence no meaning is attached to a null or
+                // empty ("") string.
+                if (TextUtils.isEmpty(networkSpecifier)) {
+                    return setNetworkSpecifier((NetworkSpecifier) null);
+                } else if (mNetworkCapabilities.hasTransport(TRANSPORT_TEST)) {
+                    return setNetworkSpecifier(new TestNetworkSpecifier(networkSpecifier));
+                } else {
+                    return setNetworkSpecifier(new EthernetNetworkSpecifier(networkSpecifier));
+                }
             }
         }
 
@@ -449,6 +493,21 @@
             }
             nc.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         }
+
+        /**
+         * Sets the optional subscription ID set.
+         * <p>
+         * This specify the subscription IDs requirement.
+         * A network will satisfy this request only if it matches one of the subIds in this set.
+         * An empty set matches all networks, including those without a subId.
+         *
+         * @param subIds A {@code Set} that represents subscription IDs.
+         */
+        @NonNull
+        public Builder setSubIds(@NonNull Set<Integer> subIds) {
+            mNetworkCapabilities.setSubIds(subIds);
+            return this;
+        }
     }
 
     // implement the Parcelable interface
@@ -487,6 +546,15 @@
     }
 
     /**
+     * Returns true iff. this NetworkRequest is of type LISTEN_FOR_BEST.
+     *
+     * @hide
+     */
+    public boolean isListenForBest() {
+        return type == Type.LISTEN_FOR_BEST;
+    }
+
+    /**
      * Returns true iff. the contained NetworkRequest is one that:
      *
      *     - should be associated with at most one satisfying network
@@ -526,6 +594,7 @@
      *
      * @hide
      */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public boolean hasUnwantedCapability(@NetCapability int capability) {
         return networkCapabilities.hasUnwantedCapability(capability);
     }
diff --git a/packages/Connectivity/framework/src/android/net/NetworkScore.java b/packages/Connectivity/framework/src/android/net/NetworkScore.java
new file mode 100644
index 0000000..6584993
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/NetworkScore.java
@@ -0,0 +1,135 @@
+/*
+ * 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.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Object representing the quality of a network as perceived by the user.
+ *
+ * A NetworkScore object represents the characteristics of a network that affects how good the
+ * network is considered for a particular use.
+ * @hide
+ */
+@SystemApi
+public final class NetworkScore implements Parcelable {
+    // This will be removed soon. Do *NOT* depend on it for any new code that is not part of
+    // a migration.
+    private final int mLegacyInt;
+
+    // Agent-managed policies
+    // TODO : add them here, starting from 1
+    /** @hide */
+    public static final int MIN_AGENT_MANAGED_POLICY = 0;
+    /** @hide */
+    public static final int MAX_AGENT_MANAGED_POLICY = -1;
+
+    // Bitmask of all the policies applied to this score.
+    private final long mPolicies;
+
+    /** @hide */
+    NetworkScore(final int legacyInt, final long policies) {
+        mLegacyInt = legacyInt;
+        mPolicies = policies;
+    }
+
+    private NetworkScore(@NonNull final Parcel in) {
+        mLegacyInt = in.readInt();
+        mPolicies = in.readLong();
+    }
+
+    public int getLegacyInt() {
+        return mLegacyInt;
+    }
+
+    /**
+     * @return whether this score has a particular policy.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public boolean hasPolicy(final int policy) {
+        return 0 != (mPolicies & (1L << policy));
+    }
+
+    @Override
+    public String toString() {
+        return "Score(" + mLegacyInt + ")";
+    }
+
+    @Override
+    public void writeToParcel(@NonNull final Parcel dest, final int flags) {
+        dest.writeInt(mLegacyInt);
+        dest.writeLong(mPolicies);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull public static final Creator<NetworkScore> CREATOR = new Creator<>() {
+        @Override
+        @NonNull
+        public NetworkScore createFromParcel(@NonNull final Parcel in) {
+            return new NetworkScore(in);
+        }
+
+        @Override
+        @NonNull
+        public NetworkScore[] newArray(int size) {
+            return new NetworkScore[size];
+        }
+    };
+
+    /**
+     * A builder for NetworkScore.
+     */
+    public static final class Builder {
+        private static final long POLICY_NONE = 0L;
+        private static final int INVALID_LEGACY_INT = Integer.MIN_VALUE;
+        private int mLegacyInt = INVALID_LEGACY_INT;
+
+        /**
+         * Sets the legacy int for this score.
+         *
+         * Do not rely on this. It will be gone by the time S is released.
+         *
+         * @param score the legacy int
+         * @return this
+         */
+        @NonNull
+        public Builder setLegacyInt(final int score) {
+            mLegacyInt = score;
+            return this;
+        }
+
+        /**
+         * Builds this NetworkScore.
+         * @return The built NetworkScore object.
+         */
+        @NonNull
+        public NetworkScore build() {
+            return new NetworkScore(mLegacyInt, POLICY_NONE);
+        }
+    }
+}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkState.java b/packages/Connectivity/framework/src/android/net/NetworkState.java
index d010265..9b69674 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkState.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkState.java
@@ -22,7 +22,7 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.Slog;
+import android.util.Log;
 
 /**
  * Snapshot of network state.
@@ -83,7 +83,7 @@
         if (VALIDATE_ROAMING_STATE && networkInfo != null && networkCapabilities != null) {
             if (networkInfo.isRoaming() == networkCapabilities
                     .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)) {
-                Slog.wtf("NetworkState", "Roaming state disagreement between " + networkInfo
+                Log.wtf("NetworkState", "Roaming state disagreement between " + networkInfo
                         + " and " + networkCapabilities);
             }
         }
diff --git a/packages/Connectivity/framework/src/android/net/NetworkUtils.java b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
index 9e42bbe..a92fda1 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkUtils.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
@@ -27,8 +27,10 @@
 import java.io.FileDescriptor;
 import java.math.BigInteger;
 import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.SocketException;
+import java.net.UnknownHostException;
 import java.util.Locale;
 import java.util.TreeSet;
 
@@ -90,7 +92,10 @@
      * Determine if {@code uid} can access network designated by {@code netId}.
      * @return {@code true} if {@code uid} can access network, {@code false} otherwise.
      */
-    public native static boolean queryUserAccess(int uid, int netId);
+    public static boolean queryUserAccess(int uid, int netId) {
+        // TODO (b/183485986): remove this method
+        return false;
+    }
 
     /**
      * DNS resolver series jni method.
@@ -212,7 +217,7 @@
     @Deprecated
     public static InetAddress numericToInetAddress(String addrString)
             throws IllegalArgumentException {
-        return InetAddress.parseNumericAddress(addrString);
+        return InetAddresses.parseNumericAddress(addrString);
     }
 
     /**
@@ -234,7 +239,7 @@
         try {
             String[] pieces = ipAndMaskString.split("/", 2);
             prefixLength = Integer.parseInt(pieces[1]);
-            address = InetAddress.parseNumericAddress(pieces[0]);
+            address = InetAddresses.parseNumericAddress(pieces[0]);
         } catch (NullPointerException e) {            // Null string.
         } catch (ArrayIndexOutOfBoundsException e) {  // No prefix length.
         } catch (NumberFormatException e) {           // Non-numeric prefix.
@@ -249,6 +254,47 @@
     }
 
     /**
+     * Utility method to parse strings such as "192.0.2.5/24" or "2001:db8::cafe:d00d/64".
+     * @hide
+     *
+     * @deprecated This method is used only for IpPrefix and LinkAddress. Since Android S, use
+     *             {@link #parseIpAndMask(String)}, if possible.
+     */
+    @Deprecated
+    public static Pair<InetAddress, Integer> legacyParseIpAndMask(String ipAndMaskString) {
+        InetAddress address = null;
+        int prefixLength = -1;
+        try {
+            String[] pieces = ipAndMaskString.split("/", 2);
+            prefixLength = Integer.parseInt(pieces[1]);
+            if (pieces[0] == null || pieces[0].isEmpty()) {
+                final byte[] bytes = new byte[16];
+                bytes[15] = 1;
+                return new Pair<InetAddress, Integer>(Inet6Address.getByAddress(
+                        "ip6-localhost"/* host */, bytes, 0 /* scope_id */), prefixLength);
+            }
+
+            if (pieces[0].startsWith("[")
+                    && pieces[0].endsWith("]")
+                    && pieces[0].indexOf(':') != -1) {
+                pieces[0] = pieces[0].substring(1, pieces[0].length() - 1);
+            }
+            address = InetAddresses.parseNumericAddress(pieces[0]);
+        } catch (NullPointerException e) {            // Null string.
+        } catch (ArrayIndexOutOfBoundsException e) {  // No prefix length.
+        } catch (NumberFormatException e) {           // Non-numeric prefix.
+        } catch (IllegalArgumentException e) {        // Invalid IP address.
+        } catch (UnknownHostException e) {            // IP address length is illegal
+        }
+
+        if (address == null || prefixLength == -1) {
+            throw new IllegalArgumentException("Invalid IP address and mask " + ipAndMaskString);
+        }
+
+        return new Pair<InetAddress, Integer>(address, prefixLength);
+    }
+
+    /**
      * Convert a 32 char hex string into a Inet6Address.
      * throws a runtime exception if the string isn't 32 chars, isn't hex or can't be
      * made into an Inet6Address
@@ -280,22 +326,7 @@
      */
     @UnsupportedAppUsage
     public static String trimV4AddrZeros(String addr) {
-        if (addr == null) return null;
-        String[] octets = addr.split("\\.");
-        if (octets.length != 4) return addr;
-        StringBuilder builder = new StringBuilder(16);
-        String result = null;
-        for (int i = 0; i < 4; i++) {
-            try {
-                if (octets[i].length() > 3) return addr;
-                builder.append(Integer.parseInt(octets[i]));
-            } catch (NumberFormatException e) {
-                return addr;
-            }
-            if (i < 3) builder.append('.');
-        }
-        result = builder.toString();
-        return result;
+        return Inet4AddressUtils.trimAddressZeros(addr);
     }
 
     /**
diff --git a/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java b/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
index 48bd297..5a76cd6 100644
--- a/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
+++ b/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
@@ -73,6 +73,14 @@
     private final Bundle mNetworkMappings;
 
     /**
+     * Return whether this object is empty.
+     * @hide
+     */
+    public boolean isEmpty() {
+        return mNetworkMappings.keySet().size() == 0;
+    }
+
+    /**
      * Return the currently built application package name to {@link OemNetworkPreference} mappings.
      * @return the current network preferences map.
      */
diff --git a/core/java/android/net/ParseException.java b/packages/Connectivity/framework/src/android/net/ParseException.java
similarity index 75%
rename from core/java/android/net/ParseException.java
rename to packages/Connectivity/framework/src/android/net/ParseException.java
index bcfdd7e..ca6d012 100644
--- a/core/java/android/net/ParseException.java
+++ b/packages/Connectivity/framework/src/android/net/ParseException.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 
 /**
  * Thrown when parsing failed.
@@ -25,12 +26,16 @@
 public class ParseException extends RuntimeException {
     public String response;
 
-    ParseException(@NonNull String response) {
+    /** @hide */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public ParseException(@NonNull String response) {
         super(response);
         this.response = response;
     }
 
-    ParseException(@NonNull String response, @NonNull Throwable cause) {
+    /** @hide */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public ParseException(@NonNull String response, @NonNull Throwable cause) {
         super(response, cause);
         this.response = response;
     }
diff --git a/packages/Connectivity/framework/src/android/net/ProxyInfo.java b/packages/Connectivity/framework/src/android/net/ProxyInfo.java
index 229db0d..745e20f 100644
--- a/packages/Connectivity/framework/src/android/net/ProxyInfo.java
+++ b/packages/Connectivity/framework/src/android/net/ProxyInfo.java
@@ -129,7 +129,7 @@
     }
 
     /**
-     * Only used in PacProxyInstaller after Local Proxy is bound.
+     * Only used in PacProxyService after Local Proxy is bound.
      * @hide
      */
     public ProxyInfo(@NonNull Uri pacFileUrl, int localProxyPort) {
diff --git a/packages/Connectivity/framework/src/android/net/QosCallbackConnection.java b/packages/Connectivity/framework/src/android/net/QosCallbackConnection.java
index bdb4ad6..de0fc24 100644
--- a/packages/Connectivity/framework/src/android/net/QosCallbackConnection.java
+++ b/packages/Connectivity/framework/src/android/net/QosCallbackConnection.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.telephony.data.NrQosSessionAttributes;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -84,6 +85,25 @@
     }
 
     /**
+     * Called when either the {@link NrQosSessionAttributes} has changed or on the first time
+     * the attributes have become available.
+     *
+     * @param session the session that is now available
+     * @param attributes the corresponding attributes of session
+     */
+    @Override
+    public void onNrQosSessionAvailable(@NonNull final QosSession session,
+            @NonNull final NrQosSessionAttributes attributes) {
+
+        mExecutor.execute(() -> {
+            final QosCallback callback = mCallback;
+            if (callback != null) {
+                callback.onQosSessionAvailable(session, attributes);
+            }
+        });
+    }
+
+    /**
      * Called when the session is lost.
      *
      * @param session the session that was lost
diff --git a/packages/Connectivity/framework/src/android/net/QosSession.java b/packages/Connectivity/framework/src/android/net/QosSession.java
index 4f3bb77..93f2ff2 100644
--- a/packages/Connectivity/framework/src/android/net/QosSession.java
+++ b/packages/Connectivity/framework/src/android/net/QosSession.java
@@ -36,6 +36,11 @@
      */
     public static final int TYPE_EPS_BEARER = 1;
 
+    /**
+     * The {@link QosSession} is a NR Session.
+     */
+    public static final int TYPE_NR_BEARER = 2;
+
     private final int mSessionId;
 
     private final int mSessionType;
@@ -100,6 +105,7 @@
      */
     @IntDef(value = {
             TYPE_EPS_BEARER,
+            TYPE_NR_BEARER,
     })
     @interface QosSessionType {}
 
diff --git a/packages/Connectivity/framework/src/android/net/QosSocketInfo.java b/packages/Connectivity/framework/src/android/net/QosSocketInfo.java
index d37c469..53d9669 100644
--- a/packages/Connectivity/framework/src/android/net/QosSocketInfo.java
+++ b/packages/Connectivity/framework/src/android/net/QosSocketInfo.java
@@ -92,7 +92,7 @@
         Objects.requireNonNull(socket, "socket cannot be null");
 
         mNetwork = Objects.requireNonNull(network, "network cannot be null");
-        mParcelFileDescriptor = ParcelFileDescriptor.dup(socket.getFileDescriptor$());
+        mParcelFileDescriptor = ParcelFileDescriptor.fromSocket(socket);
         mLocalSocketAddress =
                 new InetSocketAddress(socket.getLocalAddress(), socket.getLocalPort());
     }
@@ -114,10 +114,10 @@
         try {
             return new InetSocketAddress(InetAddress.getByAddress(address), port);
         } catch (final UnknownHostException e) {
-            /* The catch block was purposely left empty.  UnknownHostException will never be thrown
+            /* This can never happen. UnknownHostException will never be thrown
                since the address provided is numeric and non-null. */
+            throw new RuntimeException("UnknownHostException on numeric address", e);
         }
-        return new InetSocketAddress();
     }
 
     @Override
diff --git a/packages/Connectivity/framework/src/android/net/StaticIpConfiguration.java b/packages/Connectivity/framework/src/android/net/StaticIpConfiguration.java
index ce54597..7904f7a 100644
--- a/packages/Connectivity/framework/src/android/net/StaticIpConfiguration.java
+++ b/packages/Connectivity/framework/src/android/net/StaticIpConfiguration.java
@@ -24,7 +24,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import com.android.internal.util.Preconditions;
 import com.android.net.module.util.InetAddressUtils;
 
 import java.net.InetAddress;
@@ -153,7 +152,7 @@
          * @return The {@link Builder} for chaining.
          */
         public @NonNull Builder setDnsServers(@NonNull Iterable<InetAddress> dnsServers) {
-            Preconditions.checkNotNull(dnsServers);
+            Objects.requireNonNull(dnsServers);
             mDnsServers = dnsServers;
             return this;
         }
diff --git a/packages/Connectivity/framework/src/android/net/TestNetworkManager.java b/packages/Connectivity/framework/src/android/net/TestNetworkManager.java
index a174a7b..a7a6235 100644
--- a/packages/Connectivity/framework/src/android/net/TestNetworkManager.java
+++ b/packages/Connectivity/framework/src/android/net/TestNetworkManager.java
@@ -21,10 +21,9 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 
-import com.android.internal.util.Preconditions;
-
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Objects;
 
 /**
  * Class that allows creation and management of per-app, test-only networks
@@ -50,7 +49,7 @@
 
     /** @hide */
     public TestNetworkManager(@NonNull ITestNetworkManager service) {
-        mService = Preconditions.checkNotNull(service, "missing ITestNetworkManager");
+        mService = Objects.requireNonNull(service, "missing ITestNetworkManager");
     }
 
     /**
@@ -93,7 +92,7 @@
      */
     public void setupTestNetwork(
             @NonNull LinkProperties lp, boolean isMetered, @NonNull IBinder binder) {
-        Preconditions.checkNotNull(lp, "Invalid LinkProperties");
+        Objects.requireNonNull(lp, "Invalid LinkProperties");
         setupTestNetwork(lp.getInterfaceName(), lp, isMetered, new int[0], binder);
     }
 
diff --git a/packages/Connectivity/framework/src/android/net/TestNetworkSpecifier.java b/packages/Connectivity/framework/src/android/net/TestNetworkSpecifier.java
new file mode 100644
index 0000000..117457d
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/TestNetworkSpecifier.java
@@ -0,0 +1,97 @@
+/*
+ * 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.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * A {@link NetworkSpecifier} used to identify test interfaces.
+ *
+ * @see TestNetworkManager
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class TestNetworkSpecifier extends NetworkSpecifier implements Parcelable {
+
+    /**
+     * Name of the network interface.
+     */
+    @NonNull
+    private final String mInterfaceName;
+
+    public TestNetworkSpecifier(@NonNull String interfaceName) {
+        if (TextUtils.isEmpty(interfaceName)) {
+            throw new IllegalArgumentException("Empty interfaceName");
+        }
+        mInterfaceName = interfaceName;
+    }
+
+    // This may be null in the future to support specifiers based on data other than the interface
+    // name.
+    @Nullable
+    public String getInterfaceName() {
+        return mInterfaceName;
+    }
+
+    @Override
+    public boolean canBeSatisfiedBy(@Nullable NetworkSpecifier other) {
+        return equals(other);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof TestNetworkSpecifier)) return false;
+        return TextUtils.equals(mInterfaceName, ((TestNetworkSpecifier) o).mInterfaceName);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(mInterfaceName);
+    }
+
+    @Override
+    public String toString() {
+        return "TestNetworkSpecifier (" + mInterfaceName + ")";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mInterfaceName);
+    }
+
+    public static final @NonNull Creator<TestNetworkSpecifier> CREATOR =
+            new Creator<TestNetworkSpecifier>() {
+        public TestNetworkSpecifier createFromParcel(Parcel in) {
+            return new TestNetworkSpecifier(in.readString());
+        }
+        public TestNetworkSpecifier[] newArray(int size) {
+            return new TestNetworkSpecifier[size];
+        }
+    };
+}
diff --git a/packages/Connectivity/framework/src/android/net/TransportInfo.java b/packages/Connectivity/framework/src/android/net/TransportInfo.java
index aa4bbb0..fa889ea 100644
--- a/packages/Connectivity/framework/src/android/net/TransportInfo.java
+++ b/packages/Connectivity/framework/src/android/net/TransportInfo.java
@@ -29,35 +29,47 @@
 public interface TransportInfo {
 
     /**
-     * Create a copy of a {@link TransportInfo} that will preserve location sensitive fields that
-     * were set based on the permissions of the process that originally received it.
+     * Create a copy of a {@link TransportInfo} with some fields redacted based on the permissions
+     * held by the receiving app.
      *
-     * <p>By default {@link TransportInfo} does not preserve such fields during parceling, as
-     * they should not be shared outside of the process that receives them without appropriate
-     * checks.
+     * <p>
+     * Usage by connectivity stack:
+     * <ul>
+     * <li> Connectivity stack will invoke {@link #getApplicableRedactions()} to find the list
+     * of redactions that are required by this {@link TransportInfo} instance.</li>
+     * <li> Connectivity stack then loops through each bit in the bitmask returned and checks if the
+     * receiving app holds the corresponding permission.
+     * <ul>
+     * <li> If the app holds the corresponding permission, the bit is cleared from the
+     * |redactions| bitmask. </li>
+     * <li> If the app does not hold the corresponding permission, the bit is retained in the
+     * |redactions| bitmask. </li>
+     * </ul>
+     * <li> Connectivity stack then invokes {@link #makeCopy(long)} with the necessary |redactions|
+     * to create a copy to send to the corresponding app. </li>
+     * </ul>
+     * </p>
      *
-     * @param parcelLocationSensitiveFields Whether the location sensitive fields should be kept
-     *                                      when parceling
-     * @return Copy of this instance.
+     * @param redactions bitmask of redactions that needs to be performed on this instance.
+     * @return Copy of this instance with the necessary redactions.
      * @hide
      */
-    @SystemApi
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     @NonNull
-    default TransportInfo makeCopy(boolean parcelLocationSensitiveFields) {
+    default TransportInfo makeCopy(@NetworkCapabilities.RedactionType long redactions) {
         return this;
     }
 
     /**
-     * Returns whether this TransportInfo type has location sensitive fields or not (helps
-     * to determine whether to perform a location permission check or not before sending to
-     * apps).
+     * Returns a bitmask of all the applicable redactions (based on the permissions held by the
+     * receiving app) to be performed on this TransportInfo.
      *
-     * @return {@code true} if this instance contains location sensitive info, {@code false}
-     * otherwise.
+     * @return bitmask of redactions applicable on this instance.
+     * @see #makeCopy(long)
      * @hide
      */
-    @SystemApi
-    default boolean hasLocationSensitiveFields() {
-        return false;
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    default @NetworkCapabilities.RedactionType long getApplicableRedactions() {
+        return NetworkCapabilities.REDACT_NONE;
     }
 }
diff --git a/packages/Connectivity/framework/src/android/net/UidRange.java b/packages/Connectivity/framework/src/android/net/UidRange.java
index 26518d3..bc67c74 100644
--- a/packages/Connectivity/framework/src/android/net/UidRange.java
+++ b/packages/Connectivity/framework/src/android/net/UidRange.java
@@ -20,8 +20,11 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Range;
 
 import java.util.Collection;
+import java.util.Set;
 
 /**
  * An inclusive range of UIDs.
@@ -149,4 +152,32 @@
         }
         return false;
     }
+
+    /**
+     *  Convert a set of {@code Range<Integer>} to a set of {@link UidRange}.
+     */
+    @Nullable
+    public static ArraySet<UidRange> fromIntRanges(@Nullable Set<Range<Integer>> ranges) {
+        if (null == ranges) return null;
+
+        final ArraySet<UidRange> uids = new ArraySet<>();
+        for (Range<Integer> range : ranges) {
+            uids.add(new UidRange(range.getLower(), range.getUpper()));
+        }
+        return uids;
+    }
+
+    /**
+     *  Convert a set of {@link UidRange} to a set of {@code Range<Integer>}.
+     */
+    @Nullable
+    public static ArraySet<Range<Integer>> toIntRanges(@Nullable Set<UidRange> ranges) {
+        if (null == ranges) return null;
+
+        final ArraySet<Range<Integer>> uids = new ArraySet<>();
+        for (UidRange range : ranges) {
+            uids.add(new Range<Integer>(range.start, range.stop));
+        }
+        return uids;
+    }
 }
diff --git a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
index 340141b..cd8f4c0 100644
--- a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
+++ b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
@@ -22,9 +22,6 @@
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.SparseArray;
-
-import com.android.internal.util.MessageUtils;
 
 import java.util.Objects;
 
@@ -38,13 +35,10 @@
  */
 @SystemApi(client = MODULE_LIBRARIES)
 public final class VpnTransportInfo implements TransportInfo, Parcelable {
-    private static final SparseArray<String> sTypeToString =
-            MessageUtils.findMessageNames(new Class[]{VpnManager.class}, new String[]{"TYPE_VPN_"});
-
     /** Type of this VPN. */
-    @VpnManager.VpnType public final int type;
+    public final int type;
 
-    public VpnTransportInfo(@VpnManager.VpnType int type) {
+    public VpnTransportInfo(int type) {
         this.type = type;
     }
 
@@ -63,8 +57,7 @@
 
     @Override
     public String toString() {
-        final String typeString = sTypeToString.get(type, "VPN_TYPE_???");
-        return String.format("VpnTransportInfo{%s}", typeString);
+        return String.format("VpnTransportInfo{type=%d}", type);
     }
 
     @Override
diff --git a/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java b/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java
index bf5b26e..85b24713 100644
--- a/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java
@@ -19,12 +19,12 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.content.Context;
 import android.content.res.Resources;
+import android.net.ConnectivityResources;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import com.android.internal.R;
-
 /**
  * APF program support capabilities. APF stands for Android Packet Filtering and it is a flexible
  * way to drop unwanted network packets to save power.
@@ -36,6 +36,8 @@
  */
 @SystemApi
 public final class ApfCapabilities implements Parcelable {
+    private static ConnectivityResources sResources;
+
     /**
      * Version of APF instruction set supported for packet filtering. 0 indicates no support for
      * packet filtering using APF programs.
@@ -65,6 +67,14 @@
         apfPacketFormat = in.readInt();
     }
 
+    @NonNull
+    private static synchronized ConnectivityResources getResources(@NonNull Context ctx) {
+        if (sResources == null)  {
+            sResources = new ConnectivityResources(ctx);
+        }
+        return sResources;
+    }
+
 
     @Override
     public int describeContents() {
@@ -121,13 +131,43 @@
      * @return Whether the APF Filter in the device should filter out IEEE 802.3 Frames.
      */
     public static boolean getApfDrop8023Frames() {
-        return Resources.getSystem().getBoolean(R.bool.config_apfDrop802_3Frames);
+        // TODO(b/183076074): remove reading resources from system resources
+        final Resources systemRes = Resources.getSystem();
+        final int id = systemRes.getIdentifier("config_apfDrop802_3Frames", "bool", "android");
+        return systemRes.getBoolean(id);
+    }
+
+    /**
+     * @return Whether the APF Filter in the device should filter out IEEE 802.3 Frames.
+     * @hide
+     */
+    public static boolean getApfDrop8023Frames(@NonNull Context context) {
+        final ConnectivityResources res = getResources(context);
+        // TODO(b/183076074): use R.bool.config_apfDrop802_3Frames directly
+        final int id = res.get().getIdentifier("config_apfDrop802_3Frames", "bool",
+                res.getResourcesContext().getPackageName());
+        return res.get().getBoolean(id);
     }
 
     /**
      * @return An array of denylisted EtherType, packets with EtherTypes within it will be dropped.
      */
     public static @NonNull int[] getApfEtherTypeBlackList() {
-        return Resources.getSystem().getIntArray(R.array.config_apfEthTypeBlackList);
+        // TODO(b/183076074): remove reading resources from system resources
+        final Resources systemRes = Resources.getSystem();
+        final int id = systemRes.getIdentifier("config_apfEthTypeBlackList", "array", "android");
+        return systemRes.getIntArray(id);
+    }
+
+    /**
+     * @return An array of denylisted EtherType, packets with EtherTypes within it will be dropped.
+     * @hide
+     */
+    public static @NonNull int[] getApfEtherTypeDenyList(@NonNull Context context) {
+        final ConnectivityResources res = getResources(context);
+        // TODO(b/183076074): use R.array.config_apfEthTypeDenyList directly
+        final int id = res.get().getIdentifier("config_apfEthTypeDenyList", "array",
+                res.getResourcesContext().getPackageName());
+        return res.get().getIntArray(id);
     }
 }
diff --git a/packages/Connectivity/framework/src/android/net/util/KeepaliveUtils.java b/packages/Connectivity/framework/src/android/net/util/KeepaliveUtils.java
index bfc4563..8d7a0b3 100644
--- a/packages/Connectivity/framework/src/android/net/util/KeepaliveUtils.java
+++ b/packages/Connectivity/framework/src/android/net/util/KeepaliveUtils.java
@@ -19,12 +19,11 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.res.Resources;
+import android.net.ConnectivityResources;
 import android.net.NetworkCapabilities;
 import android.text.TextUtils;
 import android.util.AndroidRuntimeException;
 
-import com.android.internal.R;
-
 /**
  * Collection of utilities for socket keepalive offload.
  *
@@ -52,8 +51,11 @@
     public static int[] getSupportedKeepalives(@NonNull Context context) {
         String[] res = null;
         try {
-            res = context.getResources().getStringArray(
-                    R.array.config_networkSupportedKeepaliveCount);
+            final ConnectivityResources connRes = new ConnectivityResources(context);
+            // TODO: use R.id.config_networkSupportedKeepaliveCount directly
+            final int id = connRes.get().getIdentifier("config_networkSupportedKeepaliveCount",
+                    "array", connRes.getResourcesContext().getPackageName());
+            res = new ConnectivityResources(context).get().getStringArray(id);
         } catch (Resources.NotFoundException unused) {
         }
         if (res == null) throw new KeepaliveDeviceConfigurationException("invalid resource");
diff --git a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
index 739ddad..0b42a00 100644
--- a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
+++ b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
@@ -16,8 +16,8 @@
 
 package android.net.util;
 
-import static android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI;
-import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
+import static android.net.ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI;
+import static android.net.ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE;
 
 import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
@@ -27,6 +27,7 @@
 import android.content.IntentFilter;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.net.ConnectivityResources;
 import android.net.Uri;
 import android.os.Handler;
 import android.provider.Settings;
@@ -35,7 +36,6 @@
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
-import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.Arrays;
@@ -64,6 +64,7 @@
     private static String TAG = MultinetworkPolicyTracker.class.getSimpleName();
 
     private final Context mContext;
+    private final ConnectivityResources mResources;
     private final Handler mHandler;
     private final Runnable mAvoidBadWifiCallback;
     private final List<Uri> mSettingsUris;
@@ -107,11 +108,12 @@
 
     public MultinetworkPolicyTracker(Context ctx, Handler handler, Runnable avoidBadWifiCallback) {
         mContext = ctx;
+        mResources = new ConnectivityResources(ctx);
         mHandler = handler;
         mAvoidBadWifiCallback = avoidBadWifiCallback;
         mSettingsUris = Arrays.asList(
-            Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI),
-            Settings.Global.getUriFor(NETWORK_METERED_MULTIPATH_PREFERENCE));
+                Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI),
+                Settings.Global.getUriFor(NETWORK_METERED_MULTIPATH_PREFERENCE));
         mResolver = mContext.getContentResolver();
         mSettingObserver = new SettingObserver();
         mBroadcastReceiver = new BroadcastReceiver() {
@@ -160,12 +162,16 @@
      * Whether the device or carrier configuration disables avoiding bad wifi by default.
      */
     public boolean configRestrictsAvoidBadWifi() {
-        return (getResourcesForActiveSubId().getInteger(R.integer.config_networkAvoidBadWifi) == 0);
+        // TODO: use R.integer.config_networkAvoidBadWifi directly
+        final int id = mResources.get().getIdentifier("config_networkAvoidBadWifi",
+                "integer", mResources.getResourcesContext().getPackageName());
+        return (getResourcesForActiveSubId().getInteger(id) == 0);
     }
 
     @NonNull
     private Resources getResourcesForActiveSubId() {
-        return SubscriptionManager.getResourcesForSubId(mContext, mActiveSubId);
+        return SubscriptionManager.getResourcesForSubId(
+                mResources.getResourcesContext(), mActiveSubId);
     }
 
     /**
@@ -205,8 +211,10 @@
      * The default (device and carrier-dependent) value for metered multipath preference.
      */
     public int configMeteredMultipathPreference() {
-        return mContext.getResources().getInteger(
-                R.integer.config_networkMeteredMultipathPreference);
+        // TODO: use R.integer.config_networkMeteredMultipathPreference directly
+        final int id = mResources.get().getIdentifier("config_networkMeteredMultipathPreference",
+                "integer", mResources.getResourcesContext().getPackageName());
+        return mResources.get().getInteger(id);
     }
 
     public void updateMeteredMultipathPreference() {
diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp
index e65b7b4..1330e71 100644
--- a/packages/Connectivity/service/Android.bp
+++ b/packages/Connectivity/service/Android.bp
@@ -50,26 +50,56 @@
 }
 
 java_library {
-    name: "service-connectivity",
+    name: "service-connectivity-pre-jarjar",
     srcs: [
+        ":framework-connectivity-shared-srcs",
         ":connectivity-service-srcs",
     ],
-    installable: true,
-    jarjar_rules: "jarjar-rules.txt",
     libs: [
         "android.net.ipsec.ike",
         "services.core",
         "services.net",
         "unsupportedappusage",
+        "ServiceConnectivityResources",
     ],
     static_libs: [
         "modules-utils-os",
         "net-utils-device-common",
         "net-utils-framework-common",
         "netd-client",
+        "PlatformProperties",
+        "service-connectivity-protos",
     ],
     apex_available: [
         "//apex_available:platform",
         "com.android.tethering",
     ],
 }
+
+java_library {
+    name: "service-connectivity-protos",
+    proto: {
+        type: "nano",
+    },
+    srcs: [
+        ":system-messages-proto-src",
+    ],
+    libs: ["libprotobuf-java-nano"],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.tethering",
+    ],
+}
+
+java_library {
+    name: "service-connectivity",
+    installable: true,
+    static_libs: [
+        "service-connectivity-pre-jarjar",
+    ],
+    jarjar_rules: "jarjar-rules.txt",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.tethering",
+    ],
+}
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/Android.bp b/packages/Connectivity/service/ServiceConnectivityResources/Android.bp
new file mode 100644
index 0000000..fa4501a
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/Android.bp
@@ -0,0 +1,35 @@
+//
+// 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.
+//
+
+// APK to hold all the wifi overlayable resources.
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_app {
+    name: "ServiceConnectivityResources",
+    sdk_version: "module_current",
+    resource_dirs: [
+        "res",
+    ],
+    privileged: true,
+    export_package_resources: true,
+    apex_available: [
+        "com.android.tethering",
+    ],
+    // TODO: use a dedicated cert once generated
+    certificate: "platform",
+}
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/AndroidManifest.xml b/packages/Connectivity/service/ServiceConnectivityResources/AndroidManifest.xml
new file mode 100644
index 0000000..2c30302
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<!-- Manifest for connectivity resources APK -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.connectivity.resources"
+          coreApp="true"
+          android:versionCode="1"
+          android:versionName="S-initial">
+    <application
+        android:label="@string/connectivityResourcesAppLabel"
+        android:defaultToDeviceProtectedStorage="true"
+        android:directBootAware="true">
+        <!-- This is only used to identify this app by resolving the action.
+             The activity is never actually triggered. -->
+        <activity android:name="android.app.Activity" android:exported="true" android:enabled="true">
+            <intent-filter>
+                <action android:name="com.android.server.connectivity.intent.action.SERVICE_CONNECTIVITY_RESOURCES_APK" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/drawable-hdpi/stat_notify_rssi_in_range.png b/packages/Connectivity/service/ServiceConnectivityResources/res/drawable-hdpi/stat_notify_rssi_in_range.png
new file mode 100644
index 0000000..74977e6
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/drawable-hdpi/stat_notify_rssi_in_range.png
Binary files differ
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/drawable-mdpi/stat_notify_rssi_in_range.png b/packages/Connectivity/service/ServiceConnectivityResources/res/drawable-mdpi/stat_notify_rssi_in_range.png
new file mode 100644
index 0000000..62e4fe9
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/drawable-mdpi/stat_notify_rssi_in_range.png
Binary files differ
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/drawable-xhdpi/stat_notify_rssi_in_range.png b/packages/Connectivity/service/ServiceConnectivityResources/res/drawable-xhdpi/stat_notify_rssi_in_range.png
new file mode 100644
index 0000000..c0586d8
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/drawable-xhdpi/stat_notify_rssi_in_range.png
Binary files differ
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/drawable-xxhdpi/stat_notify_rssi_in_range.png b/packages/Connectivity/service/ServiceConnectivityResources/res/drawable-xxhdpi/stat_notify_rssi_in_range.png
new file mode 100644
index 0000000..86c34ed
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/drawable-xxhdpi/stat_notify_rssi_in_range.png
Binary files differ
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/drawable/stat_notify_wifi_in_range.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/drawable/stat_notify_wifi_in_range.xml
new file mode 100644
index 0000000..a271ca5
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/drawable/stat_notify_wifi_in_range.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="26.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="26.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#4DFFFFFF"
+        android:pathData="M19.1,14l-3.4,0l0,-1.5c0,-1.8 0.8,-2.8 1.5,-3.4C18.1,8.3 19.200001,8 20.6,8c1.2,0 2.3,0.3 3.1,0.8l1.9,-2.3C25.1,6.1 20.299999,2.1 13,2.1S0.9,6.1 0.4,6.5L13,22l0,0l0,0l0,0l0,0l6.5,-8.1L19.1,14z"/>
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M19.5,17.799999c0,-0.8 0.1,-1.3 0.2,-1.6c0.2,-0.3 0.5,-0.7 1.1,-1.2c0.4,-0.4 0.7,-0.8 1,-1.1s0.4,-0.8 0.4,-1.2c0,-0.5 -0.1,-0.9 -0.4,-1.2c-0.3,-0.3 -0.7,-0.4 -1.2,-0.4c-0.4,0 -0.8,0.1 -1.1,0.3c-0.3,0.2 -0.4,0.6 -0.4,1.1l-1.9,0c0,-1 0.3,-1.7 1,-2.2c0.6,-0.5 1.5,-0.8 2.5,-0.8c1.1,0 2,0.3 2.6,0.8c0.6,0.5 0.9,1.3 0.9,2.3c0,0.7 -0.2,1.3 -0.6,1.8c-0.4,0.6 -0.9,1.1 -1.5,1.6c-0.3,0.3 -0.5,0.5 -0.6,0.7c-0.1,0.2 -0.1,0.6 -0.1,1L19.5,17.700001zM21.4,21l-1.9,0l0,-1.8l1.9,0L21.4,21z"/>
+</vector>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-af/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-af/strings.xml
new file mode 100644
index 0000000..68720d5
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-af/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Meld aan by Wi-Fi-netwerk"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Meld by netwerk aan"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> het geen internettoegang nie"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tik vir opsies"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Selnetwerk het nie internettoegang nie"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Netwerk het nie internettoegang nie"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Daar kan nie by private DNS-bediener ingegaan word nie"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> het beperkte konnektiwiteit"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tik om in elk geval te koppel"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Het oorgeskakel na <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Toestel gebruik <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wanneer <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> geen internettoegang het nie. Heffings kan geld."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Het oorgeskakel van <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"\'n onbekende netwerktipe"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobiele data"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-am/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-am/strings.xml
new file mode 100644
index 0000000..78d9283
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-am/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"ወደ Wi-Fi አውታረ መረብ በመለያ ግባ"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"ወደ አውታረ መረብ በመለያ ይግቡ"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ምንም የበይነ መረብ መዳረሻ የለም"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ለአማራጮች መታ ያድርጉ"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"የተንቀሳቃሽ ስልክ አውታረ መረብ የበይነመረብ መዳረሻ የለውም"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"አውታረ መረብ የበይነመረብ መዳረሻ የለውም"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"የግል ዲኤንኤስ አገልጋይ ሊደረስበት አይችልም"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> የተገደበ ግንኙነት አለው"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"ለማንኛውም ለማገናኘት መታ ያድርጉ"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"ወደ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ተቀይሯል"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ምንም ዓይነት የበይነመረብ ግንኙነት በማይኖረው ጊዜ መሣሪያዎች <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ን ይጠቀማሉ። ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ።"</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"ከ<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ወደ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ተቀይሯል"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"አንድ ያልታወቀ አውታረ መረብ ዓይነት"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"የተንቀሳቃሽ ስልክ ውሂብ"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"ብሉቱዝ"</item>
+    <item msgid="1616528372438698248">"ኤተርኔት"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ar/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ar/strings.xml
new file mode 100644
index 0000000..8698a7e
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ar/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"‏تسجيل الدخول إلى شبكة Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"تسجيل الدخول إلى الشبكة"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"لا يتوفّر في <xliff:g id="NETWORK_SSID">%1$s</xliff:g> إمكانية الاتصال بالإنترنت."</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"انقر للحصول على الخيارات."</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"شبكة الجوّال هذه غير متصلة بالإنترنت"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"الشبكة غير متصلة بالإنترنت"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"لا يمكن الوصول إلى خادم أسماء نظام نطاقات خاص"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"إمكانية اتصال <xliff:g id="NETWORK_SSID">%1$s</xliff:g> محدودة."</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"يمكنك النقر للاتصال على أي حال."</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"تم التبديل إلى <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"يستخدم الجهاز <xliff:g id="NEW_NETWORK">%1$s</xliff:g> عندما لا يتوفر اتصال بالإنترنت في شبكة <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>، ويمكن أن يتم فرض رسوم مقابل ذلك."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"تم التبديل من <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> إلى <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"نوع شبكة غير معروف"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"بيانات الجوّال"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"بلوتوث"</item>
+    <item msgid="1616528372438698248">"إيثرنت"</item>
+    <item msgid="9177085807664964627">"‏شبكة افتراضية خاصة (VPN)"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-as/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-as/strings.xml
new file mode 100644
index 0000000..10b234a
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-as/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"ৱাই-ফাই নেটৱৰ্কত ছাইন ইন কৰক"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"নেটৱৰ্কত ছাইন ইন কৰক"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ৰ ইণ্টাৰনেটৰ এক্সেছ নাই"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"অধিক বিকল্পৰ বাবে টিপক"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"ম’বাইল নেটৱৰ্কৰ কোনো ইণ্টাৰনেটৰ এক্সেছ নাই"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"নেটৱৰ্কৰ কোনো ইণ্টাৰনেটৰ এক্সেছ নাই"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ব্যক্তিগত DNS ছাৰ্ভাৰ এক্সেছ কৰিব নোৱাৰি"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ৰ সকলো সেৱাৰ এক্সেছ নাই"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"যিকোনো প্ৰকাৰে সংযোগ কৰিবলৈ টিপক"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>লৈ সলনি কৰা হ’ল"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"যেতিয়া <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>ত ইণ্টাৰনেট নাথাকে, তেতিয়া ডিভাইচে <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ক ব্যৱহাৰ কৰে। মাচুল প্ৰযোজ্য হ\'ব পাৰে।"</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>ৰ পৰা <xliff:g id="NEW_NETWORK">%2$s</xliff:g> লৈ সলনি কৰা হ’ল"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"অজ্ঞাত প্ৰকাৰৰ নেটৱৰ্ক"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"ম’বাইল ডেটা"</item>
+    <item msgid="5520925862115353992">"ৱাই-ফাই"</item>
+    <item msgid="1055487873974272842">"ব্লুটুথ"</item>
+    <item msgid="1616528372438698248">"ইথাৰনেট"</item>
+    <item msgid="9177085807664964627">"ভিপিএন"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-az/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-az/strings.xml
new file mode 100644
index 0000000..d75a204
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-az/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi şəbəkəsinə daxil ol"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Şəbəkəyə daxil olun"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> üçün internet girişi əlçatan deyil"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Seçimlər üçün tıklayın"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobil şəbəkənin internetə girişi yoxdur"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Şəbəkənin internetə girişi yoxdur"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Özəl DNS serverinə giriş mümkün deyil"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> bağlantını məhdudlaşdırdı"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"İstənilən halda klikləyin"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> şəbəkə növünə keçirildi"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> şəbəkəsinin internetə girişi olmadıqda, cihaz <xliff:g id="NEW_NETWORK">%1$s</xliff:g> şəbəkəsini istifadə edir. Xidmət haqqı tutula bilər."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> şəbəkəsindən <xliff:g id="NEW_NETWORK">%2$s</xliff:g> şəbəkəsinə keçirildi"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"naməlum şəbəkə növü"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobil data"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-b+sr+Latn/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..11e4957
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Prijavljivanje na WiFi mrežu"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Prijavite se na mrežu"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nema pristup internetu"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Dodirnite za opcije"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilna mreža nema pristup internetu"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Mreža nema pristup internetu"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Pristup privatnom DNS serveru nije uspeo"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu vezu"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Dodirnite da biste se ipak povezali"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Prešli ste na tip mreže <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Uređaj koristi tip mreže <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kada tip mreže <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu. Možda će se naplaćivati troškovi."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Prešli ste sa tipa mreže <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na tip mreže <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nepoznat tip mreže"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobilni podaci"</item>
+    <item msgid="5520925862115353992">"WiFi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Eternet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-be/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-be/strings.xml
new file mode 100644
index 0000000..6b0b1f1
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-be/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Уваход у сетку Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Увайдзіце ў сетку"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> не мае доступу ў інтэрнэт"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Дакраніцеся, каб убачыць параметры"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Мабільная сетка не мае доступу ў інтэрнэт"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Сетка не мае доступу ў інтэрнэт"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Не ўдалося атрымаць доступ да прыватнага DNS-сервера"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> мае абмежаваную магчымасць падключэння"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Націсніце, каб падключыцца"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Выкананы пераход да <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Прылада выкарыстоўвае сетку <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, калі ў сетцы <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> няма доступу да інтэрнэту. Можа спаганяцца плата."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Выкананы пераход з <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> да <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"невядомы тып сеткі"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"мабільная перадача даных"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-bg/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-bg/strings.xml
new file mode 100644
index 0000000..6427bd0
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-bg/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Влизане в Wi-Fi мрежа"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Вход в мрежата"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> няма достъп до интернет"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Докоснете за опции"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Мобилната мрежа няма достъп до интернет"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Мрежата няма достъп до интернет"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Не може да се осъществи достъп до частния DNS сървър"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничена свързаност"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Докоснете, за да се свържете въпреки това"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Превключи се към <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Устройството използва <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, когато <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> няма достъп до интернет. Възможно е да бъдете таксувани."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Превключи се от <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> към <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"неизвестен тип мрежа"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"мобилни данни"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"виртуална частна мрежа (VPN)"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-bn/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-bn/strings.xml
new file mode 100644
index 0000000..74d4cd6
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-bn/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"ওয়াই-ফাই নেটওয়ার্কে সাইন-ইন করুন"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"নেটওয়ার্কে সাইন-ইন করুন"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-এর ইন্টারনেটে অ্যাক্সেস নেই"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"বিকল্পগুলির জন্য আলতো চাপুন"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"মোবাইল নেটওয়ার্কে কোনও ইন্টারনেট অ্যাক্সেস নেই"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"নেটওয়ার্কে কোনও ইন্টারনেট অ্যাক্সেস নেই"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ব্যক্তিগত ডিএনএস সার্ভার অ্যাক্সেস করা যাবে না"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-এর সীমিত কানেক্টিভিটি আছে"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"তবুও কানেক্ট করতে ট্যাপ করুন"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> এ পাল্টানো হয়েছে"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> এ ইন্টারনেট অ্যাক্সেস না থাকলে <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ব্যবহার করা হয়৷ ডেটা চার্জ প্রযোজ্য৷"</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> থেকে <xliff:g id="NEW_NETWORK">%2$s</xliff:g> এ পাল্টানো হয়েছে"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"এই নেটওয়ার্কের প্রকার অজানা"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"মোবাইল ডেটা"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"ব্লুটুথ"</item>
+    <item msgid="1616528372438698248">"ইথারনেট"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-bs/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-bs/strings.xml
new file mode 100644
index 0000000..19f6e1a
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-bs/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Prijavljivanje na WiFi mrežu"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Prijava na mrežu"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"Mreža <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nema pristup internetu"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Dodirnite za opcije"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilna mreža nema pristup internetu"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Mreža nema pristup internetu"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Nije moguće pristupiti privatnom DNS serveru"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"Mreža <xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu povezivost"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Dodirnite da se ipak povežete"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Prebačeno na: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Kada <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu, uređaj koristi mrežu <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Moguća je naplata usluge."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Prebačeno iz mreže <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> u <xliff:g id="NEW_NETWORK">%2$s</xliff:g> mrežu"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nepoznata vrsta mreže"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"prijenos podataka na mobilnoj mreži"</item>
+    <item msgid="5520925862115353992">"WiFi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ca/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ca/strings.xml
new file mode 100644
index 0000000..c55684d
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ca/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Inicia la sessió a la xarxa Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Inicia la sessió a la xarxa"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> no té accés a Internet"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Toca per veure les opcions"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"La xarxa mòbil no té accés a Internet"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"La xarxa no té accés a Internet"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"No es pot accedir al servidor DNS privat"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> té una connectivitat limitada"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Toca per connectar igualment"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Actualment en ús: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"El dispositiu utilitza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> en cas que <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> no tingui accés a Internet. És possible que s\'hi apliquin càrrecs."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Abans es feia servir la xarxa <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>; ara s\'utilitza <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"una tipus de xarxa desconegut"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"dades mòbils"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-cs/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-cs/strings.xml
new file mode 100644
index 0000000..fa8c411
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-cs/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Přihlásit se k síti Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Přihlásit se k síti"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"Síť <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nemá přístup k internetu"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Klepnutím zobrazíte možnosti"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilní síť nemá přístup k internetu"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Síť nemá přístup k internetu"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Nelze získat přístup k soukromému serveru DNS"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"Síť <xliff:g id="NETWORK_SSID">%1$s</xliff:g> umožňuje jen omezené připojení"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Klepnutím se i přesto připojíte"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Přechod na síť <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Když síť <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nebude mít přístup k internetu, zařízení použije síť <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Mohou být účtovány poplatky."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Přechod ze sítě <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na síť <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"neznámý typ sítě"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobilní data"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-da/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-da/strings.xml
new file mode 100644
index 0000000..f7be6df
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-da/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Log ind på Wi-Fi-netværk"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Log ind på netværk"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har ingen internetforbindelse"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tryk for at se valgmuligheder"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilnetværket har ingen internetadgang"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Netværket har ingen internetadgang"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Der er ikke adgang til den private DNS-server"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har begrænset forbindelse"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tryk for at oprette forbindelse alligevel"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Der blev skiftet til <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Enheden benytter <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, når der ikke er internetadgang via <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>. Der opkræves muligvis betaling."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Der blev skiftet fra <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> til <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"en ukendt netværkstype"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobildata"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-de/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-de/strings.xml
new file mode 100644
index 0000000..1e7b80c
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-de/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"In WLAN anmelden"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Im Netzwerk anmelden"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> hat keinen Internetzugriff"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Für Optionen tippen"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobiles Netzwerk hat keinen Internetzugriff"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Netzwerk hat keinen Internetzugriff"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Auf den privaten DNS-Server kann nicht zugegriffen werden"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"Schlechte Verbindung mit <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tippen, um die Verbindung trotzdem herzustellen"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Zu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> gewechselt"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Auf dem Gerät werden <xliff:g id="NEW_NETWORK">%1$s</xliff:g> genutzt, wenn über <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> kein Internet verfügbar ist. Eventuell fallen Gebühren an."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Von \"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>\" zu \"<xliff:g id="NEW_NETWORK">%2$s</xliff:g>\" gewechselt"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ein unbekannter Netzwerktyp"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"Mobile Daten"</item>
+    <item msgid="5520925862115353992">"WLAN"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-el/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-el/strings.xml
new file mode 100644
index 0000000..89647fdb
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-el/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Συνδεθείτε στο δίκτυο Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Σύνδεση στο δίκτυο"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"Η εφαρμογή <xliff:g id="NETWORK_SSID">%1$s</xliff:g> δεν έχει πρόσβαση στο διαδίκτυο"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Πατήστε για να δείτε τις επιλογές"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Το δίκτυο κινητής τηλεφωνίας δεν έχει πρόσβαση στο διαδίκτυο."</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Το δίκτυο δεν έχει πρόσβαση στο διαδίκτυο."</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Δεν είναι δυνατή η πρόσβαση στον ιδιωτικό διακομιστή DNS."</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"Το δίκτυο <xliff:g id="NETWORK_SSID">%1$s</xliff:g> έχει περιορισμένη συνδεσιμότητα"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Πατήστε για σύνδεση ούτως ή άλλως"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Μετάβαση σε δίκτυο <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Η συσκευή χρησιμοποιεί το δίκτυο <xliff:g id="NEW_NETWORK">%1$s</xliff:g> όταν το δίκτυο <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> δεν έχει πρόσβαση στο διαδίκτυο. Μπορεί να ισχύουν χρεώσεις."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Μετάβαση από το δίκτυο <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> στο δίκτυο <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"άγνωστος τύπος δικτύου"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"δεδομένα κινητής τηλεφωνίας"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rAU/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..d29e2ec
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rAU/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Sign in to a Wi-Fi network"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Sign in to network"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tap for options"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobile network has no Internet access"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Network has no Internet access"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Private DNS server cannot be accessed"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tap to connect anyway"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"an unknown network type"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobile data"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rCA/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..d29e2ec
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rCA/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Sign in to a Wi-Fi network"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Sign in to network"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tap for options"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobile network has no Internet access"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Network has no Internet access"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Private DNS server cannot be accessed"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tap to connect anyway"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"an unknown network type"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobile data"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rGB/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..d29e2ec
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rGB/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Sign in to a Wi-Fi network"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Sign in to network"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tap for options"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobile network has no Internet access"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Network has no Internet access"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Private DNS server cannot be accessed"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tap to connect anyway"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"an unknown network type"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobile data"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rIN/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..d29e2ec
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rIN/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Sign in to a Wi-Fi network"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Sign in to network"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tap for options"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobile network has no Internet access"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Network has no Internet access"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Private DNS server cannot be accessed"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tap to connect anyway"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"an unknown network type"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobile data"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rXC/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..cd69133
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rXC/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‏‎‎‏‎‎‏‏‏‎‎‎‏‏‏‎‎‏‏‏‎‎‏‎‏‎‏‎‏‎‏‏‏‏‎‎‏‎‏‎‏‎‏‏‎‏‎‏‏‏‎‎‏‎‏‎Sign in to Wi-Fi network‎‏‎‎‏‎"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎‏‏‎‎‏‎‏‎‏‎‏‏‏‎‏‎‎‎‎‏‎‎‏‏‎‎‎‎‏‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‎‏‎‏‎‏‎‎Sign in to network‎‏‎‎‏‎"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‎‏‎‏‏‏‎‏‏‎‎‏‏‎‏‎‎‏‏‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="NETWORK_SSID">%1$s</xliff:g>‎‏‎‎‏‏‏‎ has no internet access‎‏‎‎‏‎"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‏‏‏‏‏‏‎‎‎‎‎‏‎‎‏‎‎‏‏‎‏‎‏‎‎‎‎‎‏‏‎‏‎‎‎‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‏‎‎Tap for options‎‏‎‎‏‎"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‎‎‎‏‏‎‏‎‏‏‎‏‏‎‎‏‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‎‎‏‏‎‏‎‎‎‎‏‎‏‎‏‎Mobile network has no internet access‎‏‎‎‏‎"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‏‏‎‏‎‎‏‏‎‏‎‏‏‎‎‏‎‎‏‎‎‎‏‎‏‎‏‏‎‎‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‎‏‏‎‎‏‎Network has no internet access‎‏‎‎‏‎"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‏‏‎‏‎‎‏‏‎‎‏‎‎‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‏‎‏‏‎‎‏‏‏‎‎‎‏‎‎‏‎‏‎‎‎‎‏‏‏‎Private DNS server cannot be accessed‎‏‎‎‏‎"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‏‏‏‎‏‎‎‏‎‎‎‏‏‏‏‎‎‏‎‎‎‏‏‎‎‏‎‎‎‎‎‏‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‏‎‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="NETWORK_SSID">%1$s</xliff:g>‎‏‎‎‏‏‏‎ has limited connectivity‎‏‎‎‏‎"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‏‎‏‏‎‏‎‏‎‎‏‏‎‎‏‎‏‏‎‏‏‎‎‎‎‎‎‎‎‏‏‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‎‏‏‎‎‎‎‎‎Tap to connect anyway‎‏‎‎‏‎"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‏‎‎‎‎‏‎‎‏‎‎‏‎‏‏‎‎‏‏‏‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‏‏‏‎‎‏‎‎‎‏‎‎‏‏‏‏‎‎‎‎Switched to ‎‏‎‎‏‏‎<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‏‎‏‏‎‎‏‏‎‏‎‎‎‏‏‎‎‎‎‎‏‏‎‎‏‎‎‏‏‏‏‏‎‎‎‏‎‎‎‏‎‏‏‎‏‎‏‏‎‎‏‎‎‏‎‎Device uses ‎‏‎‎‏‏‎<xliff:g id="NEW_NETWORK">%1$s</xliff:g>‎‏‎‎‏‏‏‎ when ‎‏‎‎‏‏‎<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>‎‏‎‎‏‏‏‎ has no internet access. Charges may apply.‎‏‎‎‏‎"</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‏‏‏‏‎‎‏‎‏‎‏‏‏‎‏‎‎‏‎‏‎‏‎‎‎‏‎‏‎‏‏‏‏‎‏‏‏‏‏‎Switched from ‎‏‎‎‏‏‎<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to ‎‏‎‎‏‏‎<xliff:g id="NEW_NETWORK">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‎‏‏‏‏‏‎‎‏‎‏‎‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‏‎‎‏‎‏‎‎‎‎‏‏‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎an unknown network type‎‏‎‎‏‎"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‎‏‎‏‏‏‏‏‎‎‏‏‏‎‎‎‏‎‏‏‎‎‏‏‏‏‎‎‏‎‎‎‏‏‏‏‎‏‎mobile data‎‏‎‎‏‎"</item>
+    <item msgid="5520925862115353992">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‎‏‏‏‏‎‎‏‎‎‎‏‎‎‏‏‎‏‎‎‏‎‎‎‎‎‏‎‏‎‏‎‏‎‎‏‏‏‎‎‏‎‎‏‎‏‏‎‎‎‏‎‎‎‎Wi-Fi‎‏‎‎‏‎"</item>
+    <item msgid="1055487873974272842">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‏‎‎‏‎‏‏‏‎‏‏‎‎‎‏‎‏‎‎‎‎‏‎‎‎‏‎‎‎‎‏‏‏‏‏‎‎‎‎‎‎‎‎‎‏‏‎‏‎‎‏‎‏‎‎Bluetooth‎‏‎‎‏‎"</item>
+    <item msgid="1616528372438698248">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‏‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‏‎‎‎‏‏‎‎‏‎‎‎‏‎‎‏‎‎‎‎‏‎‎‎‎Ethernet‎‏‎‎‏‎"</item>
+    <item msgid="9177085807664964627">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‏‎‎‎‏‏‏‎‏‏‏‎‏‎‏‏‎‏‏‏‎‏‏‏‎‎‏‎‎‏‏‎‎‎‏‎‎‏‎‎‎‎‎‏‎‎‏‏‎VPN‎‏‎‎‏‎"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-es-rUS/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..9102dc0
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-es-rUS/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Accede a una red Wi-Fi."</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Acceder a la red"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>no tiene acceso a Internet"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Presiona para ver opciones"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"La red móvil no tiene acceso a Internet"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"La red no tiene acceso a Internet"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"No se puede acceder al servidor DNS privado"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tiene conectividad limitada"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Presiona para conectarte de todas formas"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Se cambió a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"El dispositivo usa <xliff:g id="NEW_NETWORK">%1$s</xliff:g> cuando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> no tiene acceso a Internet. Es posible que se apliquen cargos."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Se cambió de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"un tipo de red desconocido"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"Datos móviles"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-es/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-es/strings.xml
new file mode 100644
index 0000000..4c15566
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-es/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Iniciar sesión en red Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Iniciar sesión en la red"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> no tiene acceso a Internet"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Toca para ver opciones"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"La red móvil no tiene acceso a Internet"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"La red no tiene acceso a Internet"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"No se ha podido acceder al servidor DNS privado"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tiene una conectividad limitada"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Toca para conectarte de todas formas"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Se ha cambiado a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"El dispositivo utiliza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> cuando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> no tiene acceso a Internet. Es posible que se apliquen cargos."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Se ha cambiado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"tipo de red desconocido"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"datos móviles"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-et/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-et/strings.xml
new file mode 100644
index 0000000..398223a
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-et/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Logi sisse WiFi-võrku"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Võrku sisselogimine"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"Võrgul <xliff:g id="NETWORK_SSID">%1$s</xliff:g> puudub Interneti-ühendus"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Puudutage valikute nägemiseks"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobiilsidevõrgul puudub Interneti-ühendus"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Võrgul puudub Interneti-ühendus"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Privaatsele DNS-serverile ei pääse juurde"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"Võrgu <xliff:g id="NETWORK_SSID">%1$s</xliff:g> ühendus on piiratud"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Puudutage, kui soovite siiski ühenduse luua"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Lülitati võrgule <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Seade kasutab võrku <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, kui võrgul <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> puudub juurdepääs Internetile. Rakenduda võivad tasud."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Lülitati võrgult <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> võrgule <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"tundmatu võrgutüüp"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobiilne andmeside"</item>
+    <item msgid="5520925862115353992">"WiFi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-eu/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-eu/strings.xml
new file mode 100644
index 0000000..dd70316
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-eu/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Hasi saioa Wi-Fi sarean"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Hasi saioa sarean"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"Ezin da konektatu Internetera <xliff:g id="NETWORK_SSID">%1$s</xliff:g> sarearen bidez"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Sakatu aukerak ikusteko"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Sare mugikorra ezin da konektatu Internetera"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Sarea ezin da konektatu Internetera"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Ezin da atzitu DNS zerbitzari pribatua"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> sareak konektagarritasun murriztua du"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Sakatu hala ere konektatzeko"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> erabiltzen ari zara orain"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> Internetera konektatzeko gauza ez denean, <xliff:g id="NEW_NETWORK">%1$s</xliff:g> erabiltzen du gailuak. Agian kostuak ordaindu beharko dituzu."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> erabiltzen ari zinen, baina <xliff:g id="NEW_NETWORK">%2$s</xliff:g> erabiltzen ari zara orain"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"sare mota ezezaguna"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"datu-konexioa"</item>
+    <item msgid="5520925862115353992">"Wifia"</item>
+    <item msgid="1055487873974272842">"Bluetooth-a"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-fa/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-fa/strings.xml
new file mode 100644
index 0000000..46a946c
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-fa/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"‏ورود به شبکه Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"ورود به سیستم شبکه"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> به اینترنت دسترسی ندارد"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"برای گزینه‌ها ضربه بزنید"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"شبکه تلفن همراه به اینترنت دسترسی ندارد"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"شبکه به اینترنت دسترسی ندارد"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"‏سرور DNS خصوصی قابل دسترسی نیست"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> اتصال محدودی دارد"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"به‌هرصورت، برای اتصال ضربه بزنید"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"به <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> تغییر کرد"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"وقتی <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> به اینترنت دسترسی نداشته باشد، دستگاه از <xliff:g id="NEW_NETWORK">%1$s</xliff:g> استفاده می‌کند. ممکن است هزینه‌هایی اعمال شود."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"از <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> به <xliff:g id="NEW_NETWORK">%2$s</xliff:g> تغییر کرد"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"نوع شبکه نامشخص"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"داده تلفن همراه"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"بلوتوث"</item>
+    <item msgid="1616528372438698248">"اترنت"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-fi/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-fi/strings.xml
new file mode 100644
index 0000000..dd94441
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-fi/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Kirjaudu Wi-Fi-verkkoon"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Kirjaudu verkkoon"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ei ole yhteydessä internetiin"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Näytä vaihtoehdot napauttamalla."</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobiiliverkko ei ole yhteydessä internetiin"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Verkko ei ole yhteydessä internetiin"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Ei pääsyä yksityiselle DNS-palvelimelle"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> toimii rajoitetulla yhteydellä"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Yhdistä napauttamalla"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> otettiin käyttöön"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="NEW_NETWORK">%1$s</xliff:g> otetaan käyttöön, kun <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ei voi muodostaa yhteyttä internetiin. Veloitukset ovat mahdollisia."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> poistettiin käytöstä ja <xliff:g id="NEW_NETWORK">%2$s</xliff:g> otettiin käyttöön."</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"tuntematon verkon tyyppi"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobiilidata"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-fr-rCA/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..02ef50b
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-fr-rCA/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Connectez-vous au réseau Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Connectez-vous au réseau"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"Le réseau <xliff:g id="NETWORK_SSID">%1$s</xliff:g> n\'offre aucun accès à Internet"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Touchez pour afficher les options"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Le réseau cellulaire n\'offre aucun accès à Internet"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Le réseau n\'offre aucun accès à Internet"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Impossible d\'accéder au serveur DNS privé"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"Le réseau <xliff:g id="NETWORK_SSID">%1$s</xliff:g> offre une connectivité limitée"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Touchez pour vous connecter quand même"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Passé au réseau <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"L\'appareil utilise <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quand <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> n\'a pas d\'accès à Internet. Des frais peuvent s\'appliquer."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Passé du réseau <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> au <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"un type de réseau inconnu"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"données cellulaires"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"RPV"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-fr/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-fr/strings.xml
new file mode 100644
index 0000000..08c9d81
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-fr/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Connectez-vous au réseau Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Se connecter au réseau"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"Aucune connexion à Internet pour <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Appuyez ici pour afficher des options."</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Le réseau mobile ne dispose d\'aucun accès à Internet"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Le réseau ne dispose d\'aucun accès à Internet"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Impossible d\'accéder au serveur DNS privé"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"La connectivité de <xliff:g id="NETWORK_SSID">%1$s</xliff:g> est limitée"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Appuyer pour se connecter quand même"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Nouveau réseau : <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"L\'appareil utilise <xliff:g id="NEW_NETWORK">%1$s</xliff:g> lorsque <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> n\'a pas de connexion Internet. Des frais peuvent s\'appliquer."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Ancien réseau : <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>. Nouveau réseau : <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"type de réseau inconnu"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"données mobiles"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-gl/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-gl/strings.xml
new file mode 100644
index 0000000..9f98055
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-gl/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Inicia sesión na rede wifi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Inicia sesión na rede"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> non ten acceso a Internet"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Toca para ver opcións."</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"A rede de telefonía móbil non ten acceso a Internet"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"A rede non ten acceso a Internet"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Non se puido acceder ao servidor DNS privado"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"A conectividade de <xliff:g id="NETWORK_SSID">%1$s</xliff:g> é limitada"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Toca para conectarte de todas formas"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Cambiouse a: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"O dispositivo utiliza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> cando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> non ten acceso a Internet. Pódense aplicar cargos."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Cambiouse de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"un tipo de rede descoñecido"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"datos móbiles"</item>
+    <item msgid="5520925862115353992">"wifi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-gu/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-gu/strings.xml
new file mode 100644
index 0000000..4ae5a2c
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-gu/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"વાઇ-ફાઇ નેટવર્ક પર સાઇન ઇન કરો"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"નેટવર્ક પર સાઇન ઇન કરો"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ઇન્ટરનેટ ઍક્સેસ ધરાવતું નથી"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"વિકલ્પો માટે ટૅપ કરો"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"મોબાઇલ નેટવર્ક કોઈ ઇન્ટરનેટ ઍક્સેસ ધરાવતું નથી"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"નેટવર્ક કોઈ ઇન્ટરનેટ ઍક્સેસ ધરાવતું નથી"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ખાનગી DNS સર્વર ઍક્સેસ કરી શકાતા નથી"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> મર્યાદિત કનેક્ટિવિટી ધરાવે છે"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"છતાં કનેક્ટ કરવા માટે ટૅપ કરો"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> પર સ્વિચ કર્યું"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"જ્યારે <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> પાસે કોઈ ઇન્ટરનેટ ઍક્સેસ ન હોય ત્યારે ઉપકરણ <xliff:g id="NEW_NETWORK">%1$s</xliff:g>નો ઉપયોગ કરે છે. શુલ્ક લાગુ થઈ શકે છે."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> પરથી <xliff:g id="NEW_NETWORK">%2$s</xliff:g> પર સ્વિચ કર્યું"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"અજાણ્યો નેટવર્ક પ્રકાર"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"મોબાઇલ ડેટા"</item>
+    <item msgid="5520925862115353992">"વાઇ-ફાઇ"</item>
+    <item msgid="1055487873974272842">"બ્લૂટૂથ"</item>
+    <item msgid="1616528372438698248">"ઇથરનેટ"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-hi/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-hi/strings.xml
new file mode 100644
index 0000000..eff1b60
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-hi/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"वाई-फ़ाई  नेटवर्क में साइन इन करें"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"नेटवर्क में साइन इन करें"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> का इंटरनेट नहीं चल रहा है"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"विकल्पों के लिए टैप करें"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"मोबाइल नेटवर्क पर इंटरनेट ऐक्सेस नहीं है"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"इस नेटवर्क पर इंटरनेट ऐक्सेस नहीं है"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"निजी डीएनएस सर्वर को ऐक्सेस नहीं किया जा सकता"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> की कनेक्टिविटी सीमित है"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"फिर भी कनेक्ट करने के लिए टैप करें"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> पर ले जाया गया"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> में इंटरनेट की सुविधा नहीं होने पर डिवाइस <xliff:g id="NEW_NETWORK">%1$s</xliff:g> का इस्तेमाल करता है. इसके लिए शुल्क लिया जा सकता है."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> से <xliff:g id="NEW_NETWORK">%2$s</xliff:g> पर ले जाया गया"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"अज्ञात नेटवर्क प्रकार"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"मोबाइल डेटा"</item>
+    <item msgid="5520925862115353992">"वाई-फ़ाई"</item>
+    <item msgid="1055487873974272842">"ब्लूटूथ"</item>
+    <item msgid="1616528372438698248">"ईथरनेट"</item>
+    <item msgid="9177085807664964627">"वीपीएन"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-hr/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-hr/strings.xml
new file mode 100644
index 0000000..52bfb93
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-hr/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Prijava na Wi-Fi mrežu"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Prijava na mrežu"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nema pristup internetu"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Dodirnite za opcije"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilna mreža nema pristup internetu"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Mreža nema pristup internetu"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Nije moguće pristupiti privatnom DNS poslužitelju"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu povezivost"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Dodirnite da biste se ipak povezali"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Prelazak na drugu mrežu: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Kada <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu, na uređaju se upotrebljava <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Moguća je naplata naknade."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Mreža je promijenjena: <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> &gt; <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nepoznata vrsta mreže"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobilni podaci"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-hu/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-hu/strings.xml
new file mode 100644
index 0000000..2ff59b5
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-hu/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Bejelentkezés Wi-Fi hálózatba"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Bejelentkezés a hálózatba"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"A(z) <xliff:g id="NETWORK_SSID">%1$s</xliff:g> hálózaton nincs internet-hozzáférés"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Koppintson a beállítások megjelenítéséhez"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"A mobilhálózaton nincs internet-hozzáférés"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"A hálózaton nincs internet-hozzáférés"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"A privát DNS-kiszolgálóhoz nem lehet hozzáférni"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"A(z) <xliff:g id="NETWORK_SSID">%1$s</xliff:g> hálózat korlátozott kapcsolatot biztosít"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Koppintson, ha mindenképpen csatlakozni szeretne"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Átváltva erre: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="NEW_NETWORK">%1$s</xliff:g> használata, ha nincs internet-hozzáférés <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>-kapcsolaton keresztül. A szolgáltató díjat számíthat fel."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Átváltva <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>-hálózatról erre: <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ismeretlen hálózati típus"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobiladatok"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-hy/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-hy/strings.xml
new file mode 100644
index 0000000..b35d31c
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-hy/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Մուտք գործեք Wi-Fi ցանց"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Մուտք գործեք ցանց"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ցանցը չունի մուտք ինտերնետին"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Հպեք՝ ընտրանքները տեսնելու համար"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Բջջային ցանցը չի ապահովում ինտերնետ կապ"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Ցանցը միացված չէ ինտերնետին"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Մասնավոր DNS սերվերն անհասանելի է"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ցանցի կապը սահմանափակ է"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Հպեք՝ միանալու համար"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Անցել է <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ցանցի"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Երբ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ցանցում ինտերնետ կապ չի լինում, սարքն անցնում է <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ցանցի: Նման դեպքում կարող են վճարներ գանձվել:"</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ցանցից անցել է <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ցանցի"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ցանցի անհայտ տեսակ"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"բջջային ինտերնետ"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-in/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-in/strings.xml
new file mode 100644
index 0000000..27d7d89
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-in/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Login ke jaringan Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Login ke jaringan"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tidak memiliki akses internet"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Ketuk untuk melihat opsi"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Jaringan seluler tidak memiliki akses internet"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Jaringan tidak memiliki akses internet"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Server DNS pribadi tidak dapat diakses"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> memiliki konektivitas terbatas"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Ketuk untuk tetap menyambungkan"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Dialihkan ke <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Perangkat menggunakan <xliff:g id="NEW_NETWORK">%1$s</xliff:g> jika <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> tidak memiliki akses internet. Tarif mungkin berlaku."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Dialihkan dari <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ke <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"jenis jaringan yang tidak dikenal"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"data seluler"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-is/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-is/strings.xml
new file mode 100644
index 0000000..97f42dc
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-is/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Skrá inn á Wi-Fi net"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Skrá inn á net"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> er ekki með internetaðgang"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Ýttu til að sjá valkosti"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Farsímakerfið er ekki tengt við internetið"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Netkerfið er ekki tengt við internetið"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Ekki næst í DNS-einkaþjón"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"Tengigeta <xliff:g id="NETWORK_SSID">%1$s</xliff:g> er takmörkuð"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Ýttu til að tengjast samt"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Skipt yfir á <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Tækið notar <xliff:g id="NEW_NETWORK">%1$s</xliff:g> þegar <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> er ekki með internetaðgang. Gjöld kunna að eiga við."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Skipt úr <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> yfir í <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"óþekkt tegund netkerfis"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"farsímagögn"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-it/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-it/strings.xml
new file mode 100644
index 0000000..7836101
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-it/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Accedi a rete Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Accedi alla rete"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> non ha accesso a Internet"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tocca per le opzioni"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"La rete mobile non ha accesso a Internet"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"La rete non ha accesso a Internet"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Non è possibile accedere al server DNS privato"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ha una connettività limitata"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tocca per connettere comunque"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Passato a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Il dispositivo utilizza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> non ha accesso a Internet. Potrebbero essere applicati costi."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Passato da <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"tipo di rete sconosciuto"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"dati mobili"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-iw/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-iw/strings.xml
new file mode 100644
index 0000000..f322b99
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-iw/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"‏היכנס לרשת Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"היכנס לרשת"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"ל-<xliff:g id="NETWORK_SSID">%1$s</xliff:g> אין גישה לאינטרנט"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"הקש לקבלת אפשרויות"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"לרשת הסלולרית אין גישה לאינטרנט"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"לרשת אין גישה לאינטרנט"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"‏לא ניתן לגשת לשרת DNS הפרטי"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"הקישוריות של <xliff:g id="NETWORK_SSID">%1$s</xliff:g> מוגבלת"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"כדי להתחבר למרות זאת יש להקיש"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"מעבר אל <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"המכשיר משתמש ברשת <xliff:g id="NEW_NETWORK">%1$s</xliff:g> כשלרשת <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> אין גישה לאינטרנט. עשויים לחול חיובים."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"עבר מרשת <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> לרשת <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"סוג רשת לא מזוהה"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"חבילת גלישה"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ja/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ja/strings.xml
new file mode 100644
index 0000000..1aa3216
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ja/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fiネットワークにログイン"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"ネットワークにログインしてください"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> はインターネットにアクセスできません"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"タップしてその他のオプションを表示"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"モバイル ネットワークがインターネットに接続されていません"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"ネットワークがインターネットに接続されていません"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"プライベート DNS サーバーにアクセスできません"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> の接続が制限されています"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"接続するにはタップしてください"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"「<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>」に切り替えました"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"デバイスで「<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>」によるインターネット接続ができない場合に「<xliff:g id="NEW_NETWORK">%1$s</xliff:g>」を使用します。通信料が発生することがあります。"</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"「<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>」から「<xliff:g id="NEW_NETWORK">%2$s</xliff:g>」に切り替えました"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"不明なネットワーク タイプ"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"モバイルデータ"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"イーサネット"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ka/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ka/strings.xml
new file mode 100644
index 0000000..61d21b5
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ka/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi ქსელთან დაკავშირება"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"ქსელში შესვლა"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-ს არ აქვს ინტერნეტზე წვდომა"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"შეეხეთ ვარიანტების სანახავად"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"მობილურ ქსელს არ აქვს ინტერნეტზე წვდომა"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"ქსელს არ აქვს ინტერნეტზე წვდომა"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"პირად DNS სერვერზე წვდომა შეუძლებელია"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-ის კავშირები შეზღუდულია"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"შეეხეთ, თუ მაინც გსურთ დაკავშირება"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"ახლა გამოიყენება <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"თუ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ინტერნეტთან კავშირს დაკარგავს, მოწყობილობის მიერ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> იქნება გამოყენებული, რამაც შეიძლება დამატებითი ხარჯები გამოიწვიოს."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"ახლა გამოიყენება <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> (გამოიყენებოდა <xliff:g id="NEW_NETWORK">%2$s</xliff:g>)"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"უცნობი ტიპის ქსელი"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"მობილური ინტერნეტი"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-kk/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-kk/strings.xml
new file mode 100644
index 0000000..969aaef
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-kk/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi желісіне кіру"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Желіге кіру"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> желісінің интернетті пайдалану мүмкіндігі шектеулі."</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Опциялар үшін түртіңіз"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Мобильдік желі интернетке қосылмаған."</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Желі интернетке қосылмаған."</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Жеке DNS серверіне кіру мүмкін емес."</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> желісінің қосылу мүмкіндігі шектеулі."</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Бәрібір жалғау үшін түртіңіз."</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> желісіне ауысты"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Құрылғы <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> желісінде интернетпен байланыс жоғалған жағдайда <xliff:g id="NEW_NETWORK">%1$s</xliff:g> желісін пайдаланады. Деректер ақысы алынуы мүмкін."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> желісінен <xliff:g id="NEW_NETWORK">%2$s</xliff:g> желісіне ауысты"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"желі түрі белгісіз"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"мобильдік деректер"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-km/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-km/strings.xml
new file mode 100644
index 0000000..da3c337
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-km/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"ចូល​បណ្ដាញ​វ៉ាយហ្វាយ"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"ចូលទៅបណ្តាញ"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> មិនមាន​ការតភ្ជាប់អ៊ីនធឺណិត​ទេ"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ប៉ះសម្រាប់ជម្រើស"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"បណ្ដាញ​ទូរសព្ទ​ចល័ត​មិនមានការតភ្ជាប់​អ៊ីនធឺណិតទេ"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"បណ្ដាញ​មិនមាន​ការតភ្ជាប់​អ៊ីនធឺណិតទេ"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"មិនអាច​ចូលប្រើ​ម៉ាស៊ីនមេ DNS ឯកជន​បានទេ"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> មានការតភ្ជាប់​មានកម្រិត"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"មិន​អី​ទេ ចុច​​ភ្ជាប់​ចុះ"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"បានប្តូរទៅ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"ឧបករណ៍​ប្រើ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> នៅ​ពេល​ដែល <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> មិនមាន​ការ​តភ្ជាប់​អ៊ីនធឺណិត។ អាច​គិតថ្លៃ​លើការ​ប្រើប្រាស់​ទិន្នន័យ។"</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"បានប្តូរពី <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ទៅ <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ប្រភេទបណ្តាញមិនស្គាល់"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"ទិន្នន័យ​ទូរសព្ទចល័ត"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"ប៊្លូធូស"</item>
+    <item msgid="1616528372438698248">"អ៊ីសឺរណិត"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-kn/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-kn/strings.xml
new file mode 100644
index 0000000..3b5e047
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-kn/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"ವೈ-ಫೈ ನೆಟ್‍ವರ್ಕ್‌ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"ನೆಟ್‌ವರ್ಕ್‌ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕವನ್ನು ಹೊಂದಿಲ್ಲ"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ಆಯ್ಕೆಗಳಿಗೆ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"ಮೊಬೈಲ್ ನೆಟ್‌ವರ್ಕ್‌ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿಲ್ಲ"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"ನೆಟ್‌ವರ್ಕ್‌ ಇಂಟರ್ನೆಟ್‌ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿಲ್ಲ"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ಖಾಸಗಿ DNS ಸರ್ವರ್ ಅನ್ನು ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ಸೀಮಿತ ಸಂಪರ್ಕ ಕಲ್ಪಿಸುವಿಕೆಯನ್ನು ಹೊಂದಿದೆ"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"ಹೇಗಾದರೂ ಸಂಪರ್ಕಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶ ಹೊಂದಿಲ್ಲದಿರುವಾಗ, ಸಾಧನವು <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ಬಳಸುತ್ತದೆ. ಶುಲ್ಕಗಳು ಅನ್ವಯವಾಗಬಹುದು."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ರಿಂದ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ಅಪರಿಚಿತ ನೆಟ್‌ವರ್ಕ್ ಪ್ರಕಾರ"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"ಮೊಬೈಲ್ ಡೇಟಾ"</item>
+    <item msgid="5520925862115353992">"ವೈ-ಫೈ"</item>
+    <item msgid="1055487873974272842">"ಬ್ಲೂಟೂತ್‌"</item>
+    <item msgid="1616528372438698248">"ಇಥರ್ನೆಟ್"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ko/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ko/strings.xml
new file mode 100644
index 0000000..874bd75
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ko/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi 네트워크에 로그인"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"네트워크에 로그인"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>이(가) 인터넷에 액세스할 수 없습니다."</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"탭하여 옵션 보기"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"모바일 네트워크에 인터넷이 연결되어 있지 않습니다."</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"네트워크에 인터넷이 연결되어 있지 않습니다."</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"비공개 DNS 서버에 액세스할 수 없습니다."</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>에서 연결을 제한했습니다."</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"계속 연결하려면 탭하세요."</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>(으)로 전환"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>(으)로 인터넷에 연결할 수 없는 경우 기기에서 <xliff:g id="NEW_NETWORK">%1$s</xliff:g>이(가) 사용됩니다. 요금이 부과될 수 있습니다."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>에서 <xliff:g id="NEW_NETWORK">%2$s</xliff:g>(으)로 전환"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"알 수 없는 네트워크 유형"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"모바일 데이터"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"블루투스"</item>
+    <item msgid="1616528372438698248">"이더넷"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ky/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ky/strings.xml
new file mode 100644
index 0000000..1ace4dc
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ky/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi түйүнүнө кирүү"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Тармакка кирүү"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> Интернетке туташуусу жок"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Параметрлерди ачуу үчүн таптап коюңуз"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Мобилдик Интернет жок"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Тармактын Интернет жок"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Жеке DNS сервери жеткиликсиз"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> байланышы чектелген"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Баары бир туташуу үчүн таптаңыз"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> тармагына которуштурулду"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> тармагы Интернетке туташпай турганда, түзмөгүңүз <xliff:g id="NEW_NETWORK">%1$s</xliff:g> тармагын колдонот. Акы алынышы мүмкүн."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> дегенден <xliff:g id="NEW_NETWORK">%2$s</xliff:g> тармагына которуштурулду"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"белгисиз тармак түрү"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"мобилдик трафик"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-lo/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-lo/strings.xml
new file mode 100644
index 0000000..3db497e
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-lo/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"ເຂົ້າສູ່ລະບົບເຄືອຂ່າຍ Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"ລົງຊື່ເຂົ້າເຄືອຂ່າຍ"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ແຕະເພື່ອເບິ່ງຕົວເລືອກ"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"ເຄືອຂ່າຍມືຖືບໍ່ສາມາດເຂົ້າເຖິງອິນເຕີເນັດໄດ້"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"ເຄືອຂ່າຍບໍ່ສາມາດເຂົ້າເຖິງອິນເຕີເນັດໄດ້"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ບໍ່ສາມາດເຂົ້າເຖິງເຊີບເວີ DNS ສ່ວນຕົວໄດ້"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ມີການເຊື່ອມຕໍ່ທີ່ຈຳກັດ"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"ແຕະເພື່ອຢືນຢັນການເຊື່ອມຕໍ່"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"ສະຫຼັບໄປໃຊ້ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ແລ້ວ"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"ອຸປະກອນຈະໃຊ້ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ເມື່ອ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ. ອາດມີການຮຽກເກັບຄ່າບໍລິການ."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"ສະຫຼັບຈາກ <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ໄປໃຊ້ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ແລ້ວ"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ບໍ່ຮູ້ຈັກປະເພດເຄືອຂ່າຍ"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"ອິນເຕີເນັດມືຖື"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"ອີເທີເນັດ"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-lt/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-lt/strings.xml
new file mode 100644
index 0000000..c639c1f
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-lt/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Prisijungti prie „Wi-Fi“ tinklo"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Prisijungti prie tinklo"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"„<xliff:g id="NETWORK_SSID">%1$s</xliff:g>“ negali pasiekti interneto"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Palieskite, kad būtų rodomos parinktys."</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobiliojo ryšio tinkle nėra prieigos prie interneto"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Tinkle nėra prieigos prie interneto"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Privataus DNS serverio negalima pasiekti"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"„<xliff:g id="NETWORK_SSID">%1$s</xliff:g>“ ryšys apribotas"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Palieskite, jei vis tiek norite prisijungti"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Perjungta į tinklą <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Įrenginyje naudojamas kitas tinklas (<xliff:g id="NEW_NETWORK">%1$s</xliff:g>), kai dabartiniame tinkle (<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>) nėra interneto ryšio. Gali būti taikomi mokesčiai."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Perjungta iš tinklo <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> į tinklą <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nežinomas tinklo tipas"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobiliojo ryšio duomenys"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Eternetas"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-lv/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-lv/strings.xml
new file mode 100644
index 0000000..5774603
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-lv/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Pierakstieties Wi-Fi tīklā"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Pierakstīšanās tīklā"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"Tīklā <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nav piekļuves internetam"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Pieskarieties, lai skatītu iespējas."</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilajā tīklā nav piekļuves internetam."</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Tīklā nav piekļuves internetam."</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Nevar piekļūt privātam DNS serverim."</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"Tīklā <xliff:g id="NETWORK_SSID">%1$s</xliff:g> ir ierobežota savienojamība"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Lai tik un tā izveidotu savienojumu, pieskarieties"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Pārslēdzās uz tīklu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Kad vienā tīklā (<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>) nav piekļuves internetam, ierīcē tiek izmantots cits tīkls (<xliff:g id="NEW_NETWORK">%1$s</xliff:g>). Var tikt piemērota maksa."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Pārslēdzās no tīkla <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> uz tīklu <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nezināms tīkla veids"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobilie dati"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc204-mnc04/config.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc204-mnc04/config.xml
new file mode 100644
index 0000000..7e7025f
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc204-mnc04/config.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<!-- Configuration values for ConnectivityService
+     DO NOT EDIT THIS FILE for specific device configuration; instead, use a Runtime Resources
+     Overlay package following the overlayable.xml configuration in the same directory:
+     https://source.android.com/devices/architecture/rros -->
+<resources>
+    <!-- Whether the device should automatically switch away from Wi-Fi networks that lose
+         Internet access. Actual device behaviour is controlled by
+         Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. -->
+    <integer translatable="false" name="config_networkAvoidBadWifi">0</integer>
+</resources>
\ No newline at end of file
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc310-mnc004/config.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc310-mnc004/config.xml
new file mode 100644
index 0000000..7e7025f
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc310-mnc004/config.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<!-- Configuration values for ConnectivityService
+     DO NOT EDIT THIS FILE for specific device configuration; instead, use a Runtime Resources
+     Overlay package following the overlayable.xml configuration in the same directory:
+     https://source.android.com/devices/architecture/rros -->
+<resources>
+    <!-- Whether the device should automatically switch away from Wi-Fi networks that lose
+         Internet access. Actual device behaviour is controlled by
+         Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. -->
+    <integer translatable="false" name="config_networkAvoidBadWifi">0</integer>
+</resources>
\ No newline at end of file
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc311-mnc480/config.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc311-mnc480/config.xml
new file mode 100644
index 0000000..7e7025f
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc311-mnc480/config.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<!-- Configuration values for ConnectivityService
+     DO NOT EDIT THIS FILE for specific device configuration; instead, use a Runtime Resources
+     Overlay package following the overlayable.xml configuration in the same directory:
+     https://source.android.com/devices/architecture/rros -->
+<resources>
+    <!-- Whether the device should automatically switch away from Wi-Fi networks that lose
+         Internet access. Actual device behaviour is controlled by
+         Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. -->
+    <integer translatable="false" name="config_networkAvoidBadWifi">0</integer>
+</resources>
\ No newline at end of file
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-mk/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mk/strings.xml
new file mode 100644
index 0000000..053c7cf
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mk/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Најавете се на мрежа на Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Најавете се на мрежа"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> нема интернет-пристап"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Допрете за опции"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Мобилната мрежа нема интернет-пристап"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Мрежата нема интернет-пристап"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Не може да се пристапи до приватниот DNS-сервер"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничена поврзливост"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Допрете за да се поврзете и покрај тоа"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Префрлено на <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Уредот користи <xliff:g id="NEW_NETWORK">%1$s</xliff:g> кога <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> нема пристап до интернет. Може да се наплатат трошоци."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Префрлено од <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"непознат тип мрежа"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"мобилен интернет"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Етернет"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ml/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ml/strings.xml
new file mode 100644
index 0000000..3e80cf1
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ml/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"വൈഫൈ നെറ്റ്‌വർക്കിലേക്ക് സൈൻ ഇൻ ചെയ്യുക"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"നെറ്റ്‌വർക്കിലേക്ക് സൈൻ ഇൻ ചെയ്യുക"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> എന്നതിന് ഇന്റർനെറ്റ് ആക്‌സസ് ഇല്ല"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ഓപ്ഷനുകൾക്ക് ടാപ്പുചെയ്യുക"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"മൊബെെൽ നെറ്റ്‌വർക്കിന് ഇന്റർനെറ്റ് ആക്‌സസ് ഇല്ല"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"നെറ്റ്‌വർക്കിന് ഇന്റർനെറ്റ് ആക്‌സസ് ഇല്ല"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"സ്വകാര്യ DNS സെർവർ ആക്‌സസ് ചെയ്യാനാവില്ല"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> എന്നതിന് പരിമിതമായ കണക്റ്റിവിറ്റി ഉണ്ട്"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"ഏതുവിധേനയും കണക്‌റ്റ് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> എന്നതിലേക്ക് മാറി"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>-ന് ഇന്റർനെറ്റ് ആക്‌സസ് ഇല്ലാത്തപ്പോൾ ഉപകരണം <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ഉപയോഗിക്കുന്നു. നിരക്കുകൾ ബാധകമായേക്കാം."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> നെറ്റ്‌വർക്കിൽ നിന്ന് <xliff:g id="NEW_NETWORK">%2$s</xliff:g> നെറ്റ്‌വർക്കിലേക്ക് മാറി"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"തിരിച്ചറിയാനാകാത്ത ഒരു നെറ്റ്‌വർക്ക് തരം"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"മൊബൈൽ ഡാറ്റ"</item>
+    <item msgid="5520925862115353992">"വൈഫൈ"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"ഇതര്‍നെറ്റ്"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-mn/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mn/strings.xml
new file mode 100644
index 0000000..214fc0c
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mn/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi сүлжээнд нэвтэрнэ үү"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Сүлжээнд нэвтэрнэ үү"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-д интернэтийн хандалт алга"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Сонголт хийхийн тулд товшино уу"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Мобайл сүлжээнд интернэт хандалт байхгүй байна"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Сүлжээнд интернэт хандалт байхгүй байна"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Хувийн DNS серверт хандах боломжгүй байна"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> зарим үйлчилгээнд хандах боломжгүй байна"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Ямар ч тохиолдолд холбогдохын тулд товших"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> руу шилжүүлсэн"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> интернет холболтгүй үед төхөөрөмж <xliff:g id="NEW_NETWORK">%1$s</xliff:g>-г ашигладаг. Төлбөр гарч болзошгүй."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>-с <xliff:g id="NEW_NETWORK">%2$s</xliff:g> руу шилжүүлсэн"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"сүлжээний тодорхойгүй төрөл"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"мобайл дата"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Этернэт"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-mr/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mr/strings.xml
new file mode 100644
index 0000000..c4b1998
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mr/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"वाय-फाय नेटवर्कमध्‍ये साइन इन करा"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"नेटवर्कवर साइन इन करा"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ला इंटरनेट अ‍ॅक्सेस नाही"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"पर्यायांसाठी टॅप करा"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"मोबाइल नेटवर्कला इंटरनेट ॲक्सेस नाही"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"नेटवर्कला इंटरनेट ॲक्सेस नाही"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"खाजगी DNS सर्व्हर ॲक्सेस करू शकत नाही"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ला मर्यादित कनेक्टिव्हिटी आहे"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"तरीही कनेक्ट करण्यासाठी टॅप करा"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> वर स्विच केले"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> कडे इंटरनेटचा अ‍ॅक्सेस नसताना डिव्हाइस <xliff:g id="NEW_NETWORK">%1$s</xliff:g> वापरते. शुल्क लागू शकते."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> वरून <xliff:g id="NEW_NETWORK">%2$s</xliff:g> वर स्विच केले"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"अज्ञात नेटवर्क प्रकार"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"मोबाइल डेटा"</item>
+    <item msgid="5520925862115353992">"वाय-फाय"</item>
+    <item msgid="1055487873974272842">"ब्लूटूथ"</item>
+    <item msgid="1616528372438698248">"इथरनेट"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ms/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ms/strings.xml
new file mode 100644
index 0000000..529d0bd
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ms/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Log masuk ke rangkaian Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Log masuk ke rangkaian"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tiada akses Internet"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Ketik untuk mendapatkan pilihan"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Rangkaian mudah alih tiada akses Internet"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Rangkaian tiada akses Internet"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Pelayan DNS peribadi tidak boleh diakses"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> mempunyai kesambungan terhad"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Ketik untuk menyambung juga"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Beralih kepada <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Peranti menggunakan <xliff:g id="NEW_NETWORK">%1$s</xliff:g> apabila <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> tiada akses Internet. Bayaran mungkin dikenakan."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Beralih daripada <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> kepada <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"jenis rangkaian tidak diketahui"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"data mudah alih"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-my/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-my/strings.xml
new file mode 100644
index 0000000..50590fa
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-my/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"ဝိုင်ဖိုင်ကွန်ရက်သို့ လက်မှတ်ထိုးဝင်ပါ"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"ကွန်ယက်သို့ လက်မှတ်ထိုးဝင်ရန်"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> တွင် အင်တာနက်အသုံးပြုခွင့် မရှိပါ"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"အခြားရွေးချယ်စရာများကိုကြည့်ရန် တို့ပါ"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"မိုဘိုင်းကွန်ရက်တွင် အင်တာနက်ချိတ်ဆက်မှု မရှိပါ"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"ကွန်ရက်တွင် အင်တာနက်အသုံးပြုခွင့် မရှိပါ"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"သီးသန့် ဒီအန်အက်စ် (DNS) ဆာဗာကို သုံး၍မရပါ။"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> တွင် ချိတ်ဆက်မှုကို ကန့်သတ်ထားသည်"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"မည်သို့ပင်ဖြစ်စေ ချိတ်ဆက်ရန် တို့ပါ"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> သို့ ပြောင်းလိုက်ပြီ"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ဖြင့် အင်တာနက် အသုံးမပြုနိုင်သည့်အချိန်တွင် စက်ပစ္စည်းသည် <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ကို သုံးပါသည်။ ဒေတာသုံးစွဲခ ကျသင့်နိုင်ပါသည်။"</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> မှ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> သို့ ပြောင်းလိုက်ပြီ"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"အမည်မသိကွန်ရက်အမျိုးအစား"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"မိုဘိုင်းဒေတာ"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"ဘလူးတုသ်"</item>
+    <item msgid="1616528372438698248">"အီသာနက်"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-nb/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-nb/strings.xml
new file mode 100644
index 0000000..b89d198
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-nb/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Logg på Wi-Fi-nettverket"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Logg på nettverk"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har ingen internettilkobling"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Trykk for å få alternativer"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilnettverket har ingen internettilgang"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Nettverket har ingen internettilgang"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Den private DNS-tjeneren kan ikke nås"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har begrenset tilkobling"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Trykk for å koble til likevel"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Byttet til <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Enheten bruker <xliff:g id="NEW_NETWORK">%1$s</xliff:g> når <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ikke har Internett-tilgang. Avgifter kan påløpe."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Byttet fra <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> til <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"en ukjent nettverkstype"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobildata"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ne/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ne/strings.xml
new file mode 100644
index 0000000..bdcfa3b
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ne/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi नेटवर्कमा साइन इन गर्नुहोस्"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"सञ्जालमा साइन इन गर्नुहोस्"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> को इन्टरनेटमाथि पहुँच छैन"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"विकल्पहरूका लागि ट्याप गर्नुहोस्"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"मोबाइल नेटवर्कको इन्टरनेटमाथि पहुँच छैन"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"नेटवर्कको इन्टरनेटमाथि पहुँच छैन"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"निजी DNS सर्भरमाथि पहुँच प्राप्त गर्न सकिँदैन"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> को जडान सीमित छ"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"जसरी भए पनि जडान गर्न ट्याप गर्नुहोस्"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> मा बदल्नुहोस्"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> मार्फत इन्टरनेटमाथि पहुँच राख्न नसकेको अवस्थामा यन्त्रले <xliff:g id="NEW_NETWORK">%1$s</xliff:g> प्रयोग गर्दछ। शुल्क लाग्न सक्छ।"</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> बाट <xliff:g id="NEW_NETWORK">%2$s</xliff:g> मा परिवर्तन गरियो"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"नेटवर्कको कुनै अज्ञात प्रकार"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"मोबाइल डेटा"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"ब्लुटुथ"</item>
+    <item msgid="1616528372438698248">"इथरनेट"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-nl/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-nl/strings.xml
new file mode 100644
index 0000000..8ecff6e
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-nl/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Inloggen bij wifi-netwerk"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Inloggen bij netwerk"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> heeft geen internettoegang"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tik voor opties"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobiel netwerk heeft geen internettoegang"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Netwerk heeft geen internettoegang"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Geen toegang tot privé-DNS-server"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> heeft beperkte connectiviteit"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tik om toch verbinding te maken"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Overgeschakeld naar <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Apparaat gebruikt <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wanneer <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> geen internetverbinding heeft. Er kunnen kosten in rekening worden gebracht."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Overgeschakeld van <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> naar <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"een onbekend netwerktype"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobiele data"</item>
+    <item msgid="5520925862115353992">"Wifi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-or/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-or/strings.xml
new file mode 100644
index 0000000..6ec1f9d
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-or/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"ୱାଇ-ଫାଇ ନେଟୱର୍କରେ ସାଇନ୍‍-ଇନ୍‍ କରନ୍ତୁ"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"ନେଟ୍‌ୱର୍କରେ ସାଇନ୍‍ ଇନ୍‍ କରନ୍ତୁ"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ର ଇଣ୍ଟର୍ନେଟ୍ ଆକ୍ସେସ୍ ନାହିଁ"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ବିକଳ୍ପ ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"ମୋବାଇଲ୍ ନେଟ୍‌ୱାର୍କରେ ଇଣ୍ଟର୍ନେଟ୍ ଆକ୍ସେସ୍ ନାହିଁ"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"ନେଟ୍‌ୱାର୍କରେ ଇଣ୍ଟର୍ନେଟ୍ ଆକ୍ସେସ୍ ନାହିଁ"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ବ୍ୟକ୍ତିଗତ DNS ସର୍ଭର୍ ଆକ୍ସେସ୍ କରିହେବ ନାହିଁ"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ର ସୀମିତ ସଂଯୋଗ ଅଛି"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"ତଥାପି ଯୋଗାଯୋଗ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>କୁ ବଦଳାଗଲା"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>ର ଇଣ୍ଟରନେଟ୍‍ ଆକ୍ସେସ୍ ନଥିବାବେଳେ ଡିଭାଇସ୍‍ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ବ୍ୟବହାର କରିଥାଏ। ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ।"</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ରୁ <xliff:g id="NEW_NETWORK">%2$s</xliff:g>କୁ ବଦଳାଗଲା"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ଏକ ଅଜଣା ନେଟ୍‌ୱର୍କ ପ୍ରକାର"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"ମୋବାଇଲ୍‌ ଡାଟା"</item>
+    <item msgid="5520925862115353992">"ୱାଇ-ଫାଇ"</item>
+    <item msgid="1055487873974272842">"ବ୍ଲୁଟୁଥ"</item>
+    <item msgid="1616528372438698248">"ଇଥରନେଟ୍‌"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-pa/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-pa/strings.xml
new file mode 100644
index 0000000..e948193
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-pa/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"ਨੈੱਟਵਰਕ \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ਕੋਲ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ਵਿਕਲਪਾਂ ਲਈ ਟੈਪ ਕਰੋ"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"ਮੋਬਾਈਲ ਨੈੱਟਵਰਕ ਕੋਲ ਇੰਟਰਨੈੱਟ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"ਨੈੱਟਵਰਕ ਕੋਲ ਇੰਟਰਨੈੱਟ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ਨਿੱਜੀ ਡੋਮੇਨ ਨਾਮ ਪ੍ਰਣਾਲੀ (DNS) ਸਰਵਰ \'ਤੇ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ਕੋਲ ਸੀਮਤ ਕਨੈਕਟੀਵਿਟੀ ਹੈ"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"ਫਿਰ ਵੀ ਕਨੈਕਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"ਬਦਲਕੇ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ਲਿਆਂਦਾ ਗਿਆ"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ਦੀ ਇੰਟਰਨੈੱਟ \'ਤੇ ਪਹੁੰਚ ਨਾ ਹੋਣ \'ਤੇ ਡੀਵਾਈਸ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰਦਾ ਹੈ। ਖਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ।"</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ਤੋਂ ਬਦਲਕੇ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> \'ਤੇ ਕੀਤਾ ਗਿਆ"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ਇੱਕ ਅਗਿਆਤ ਨੈੱਟਵਰਕ ਕਿਸਮ"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"ਮੋਬਾਈਲ ਡਾਟਾ"</item>
+    <item msgid="5520925862115353992">"ਵਾਈ-ਫਾਈ"</item>
+    <item msgid="1055487873974272842">"ਬਲੂਟੁੱਥ"</item>
+    <item msgid="1616528372438698248">"ਈਥਰਨੈੱਟ"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-pl/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-pl/strings.xml
new file mode 100644
index 0000000..038328f
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-pl/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Zaloguj się w sieci Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Zaloguj się do sieci"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nie ma dostępu do internetu"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Kliknij, by wyświetlić opcje"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Sieć komórkowa nie ma dostępu do internetu"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Sieć nie ma dostępu do internetu"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Brak dostępu do prywatnego serwera DNS"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ma ograniczoną łączność"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Kliknij, by mimo to nawiązać połączenie"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Zmieniono na połączenie typu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Urządzenie korzysta z połączenia typu <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, gdy <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nie dostępu do internetu. Mogą zostać naliczone opłaty."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Przełączono z połączenia typu <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na <xliff:g id="NEW_NETWORK">%2$s</xliff:g>."</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nieznany typ sieci"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobilna transmisja danych"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-pt-rBR/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..fe37405
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-pt-rBR/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Fazer login na rede Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Fazer login na rede"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> não tem acesso à Internet"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Toque para ver opções"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"A rede móvel não tem acesso à Internet"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"A rede não tem acesso à Internet"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Não é possível acessar o servidor DNS privado"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tem conectividade limitada"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Toque para conectar mesmo assim"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Alternado para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"O dispositivo usa <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> não tem acesso à Internet. Esse serviço pode ser cobrado."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Alternado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"um tipo de rede desconhecido"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"dados móveis"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-pt-rPT/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..69060f7
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-pt-rPT/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Iniciar sessão na rede Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Início de sessão na rede"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> não tem acesso à Internet"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Toque para obter mais opções"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"A rede móvel não tem acesso à Internet"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"A rede não tem acesso à Internet"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Não é possível aceder ao servidor DNS."</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tem conetividade limitada."</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Toque para ligar mesmo assim."</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Mudou para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"O dispositivo utiliza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> não tem acesso à Internet. Podem aplicar-se custos."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Mudou de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"um tipo de rede desconhecido"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"dados móveis"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-pt/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-pt/strings.xml
new file mode 100644
index 0000000..fe37405
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-pt/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Fazer login na rede Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Fazer login na rede"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> não tem acesso à Internet"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Toque para ver opções"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"A rede móvel não tem acesso à Internet"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"A rede não tem acesso à Internet"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Não é possível acessar o servidor DNS privado"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tem conectividade limitada"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Toque para conectar mesmo assim"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Alternado para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"O dispositivo usa <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> não tem acesso à Internet. Esse serviço pode ser cobrado."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Alternado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"um tipo de rede desconhecido"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"dados móveis"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ro/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ro/strings.xml
new file mode 100644
index 0000000..227b7be
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ro/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Conectați-vă la rețeaua Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Conectați-vă la rețea"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nu are acces la internet"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Atingeți pentru opțiuni"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Rețeaua mobilă nu are acces la internet"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Rețeaua nu are acces la internet"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Serverul DNS privat nu poate fi accesat"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> are conectivitate limitată"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Atingeți pentru a vă conecta oricum"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"S-a comutat la <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Dispozitivul folosește <xliff:g id="NEW_NETWORK">%1$s</xliff:g> când <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nu are acces la internet. Se pot aplica taxe."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"S-a comutat de la <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> la <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"un tip de rețea necunoscut"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"date mobile"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ru/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ru/strings.xml
new file mode 100644
index 0000000..18acf81
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ru/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Подключение к Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Регистрация в сети"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"Сеть \"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>\" не подключена к Интернету"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Нажмите, чтобы показать варианты."</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Мобильная сеть не подключена к Интернету"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Сеть не подключена к Интернету"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Доступа к частному DNS-серверу нет."</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"Подключение к сети \"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>\" ограничено"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Нажмите, чтобы подключиться"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Новое подключение: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Устройство использует <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, если подключение к сети <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> недоступно. Может взиматься плата за передачу данных."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Устройство отключено от сети <xliff:g id="NEW_NETWORK">%2$s</xliff:g> и теперь использует <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"неизвестный тип сети"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"мобильный Интернет"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-si/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-si/strings.xml
new file mode 100644
index 0000000..6307c2b
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-si/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi ජාලයට පුරනය වන්න"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"ජාලයට පුරනය වන්න"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> හට අන්තර්ජාල ප්‍රවේශය නැත"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"විකල්ප සඳහා තට්ටු කරන්න"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"ජංගම ජාලවලට අන්තර්ජාල ප්‍රවේශය නැත"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"ජාලයට අන්තර්ජාල ප්‍රවේශය නැත"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"පුද්ගලික DNS සේවාදායකයට ප්‍රවේශ වීමට නොහැකිය"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> හට සීමිත සබැඳුම් හැකියාවක් ඇත"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"කෙසේ වෙතත් ඉදිරියට යාමට තට්ටු කරන්න"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> වෙත මාරු විය"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"උපාංගය <xliff:g id="NEW_NETWORK">%1$s</xliff:g> <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> සඳහා අන්තර්ජාල ප්‍රවේශය නැති විට භාවිත කරයි. ගාස්තු අදාළ විය හැකිය."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> සිට <xliff:g id="NEW_NETWORK">%2$s</xliff:g> වෙත මාරු විය"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"නොදන්නා ජාල වර්ගයකි"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"ජංගම දත්ත"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"බ්ලූටූත්"</item>
+    <item msgid="1616528372438698248">"ඊතර්නෙට්"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-sk/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sk/strings.xml
new file mode 100644
index 0000000..e894fef
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sk/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Prihlásiť sa do siete Wi‑Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Prihlásenie do siete"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nemá prístup k internetu"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Klepnutím získate možnosti"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilná sieť nemá prístup k internetu"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Sieť nemá prístup k internetu"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"K súkromnému serveru DNS sa nepodarilo získať prístup"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> má obmedzené pripojenie"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Ak sa chcete aj napriek tomu pripojiť, klepnite"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Prepnuté na sieť: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Keď <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nemá prístup k internetu, zariadenie používa <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Môžu sa účtovať poplatky."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Prepnuté zo siete <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na sieť <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"neznámy typ siete"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobilné dáta"</item>
+    <item msgid="5520925862115353992">"Wi‑Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-sl/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sl/strings.xml
new file mode 100644
index 0000000..954b324
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sl/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Prijavite se v omrežje Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Prijava v omrežje"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"Omrežje <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nima dostopa do interneta"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Dotaknite se za možnosti"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilno omrežje nima dostopa do interneta"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Omrežje nima dostopa do interneta"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Do zasebnega strežnika DNS ni mogoče dostopati"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"Povezljivost omrežja <xliff:g id="NETWORK_SSID">%1$s</xliff:g> je omejena"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Dotaknite se, da kljub temu vzpostavite povezavo"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Preklopljeno na omrežje vrste <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Naprava uporabi omrežje vrste <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, ko omrežje vrste <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nima dostopa do interneta. Prenos podatkov se lahko zaračuna."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Preklopljeno z omrežja vrste <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na omrežje vrste <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"neznana vrsta omrežja"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"prenos podatkov v mobilnem omrežju"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-sq/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sq/strings.xml
new file mode 100644
index 0000000..bd5d052
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sq/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Identifikohu në rrjetin Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Identifikohu në rrjet"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nuk ka qasje në internet"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Trokit për opsionet"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Rrjeti celular nuk ka qasje në internet"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Rrjeti nuk ka qasje në internet"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Serveri privat DNS nuk mund të qaset"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ka lidhshmëri të kufizuar"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Trokit për t\'u lidhur gjithsesi"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Kaloi te <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Pajisja përdor <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kur <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nuk ka qasje në internet. Mund të zbatohen tarifa."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Kaloi nga <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> te <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"një lloj rrjeti i panjohur"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"të dhënat celulare"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Eternet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-sr/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sr/strings.xml
new file mode 100644
index 0000000..8bedbff
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sr/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Пријављивање на WiFi мрежу"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Пријавите се на мрежу"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> нема приступ интернету"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Додирните за опције"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Мобилна мрежа нема приступ интернету"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Мрежа нема приступ интернету"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Приступ приватном DNS серверу није успео"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничену везу"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Додирните да бисте се ипак повезали"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Прешли сте на тип мреже <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Уређај користи тип мреже <xliff:g id="NEW_NETWORK">%1$s</xliff:g> када тип мреже <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> нема приступ интернету. Можда ће се наплаћивати трошкови."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Прешли сте са типа мреже <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на тип мреже <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"непознат тип мреже"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"мобилни подаци"</item>
+    <item msgid="5520925862115353992">"WiFi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Етернет"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-sv/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sv/strings.xml
new file mode 100644
index 0000000..b3f1763
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sv/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Logga in på ett Wi-Fi-nätverk"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Logga in på nätverket"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har ingen internetanslutning"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tryck för alternativ"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilnätverket har ingen internetanslutning"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Nätverket har ingen internetanslutning"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Det går inte att komma åt den privata DNS-servern."</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har begränsad anslutning"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tryck för att ansluta ändå"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Byte av nätverk till <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="NEW_NETWORK">%1$s</xliff:g> används på enheten när det inte finns internetåtkomst via <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>. Avgifter kan tillkomma."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Byte av nätverk från <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> till <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"en okänd nätverkstyp"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobildata"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-sw/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sw/strings.xml
new file mode 100644
index 0000000..9674654
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sw/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Ingia kwa mtandao wa Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Ingia katika mtandao"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> haina uwezo wa kufikia intaneti"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Gusa ili upate chaguo"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mtandao wa simu hauna uwezo wa kufikia intaneti"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Mtandao hauna uwezo wa kufikia intaneti"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Seva ya faragha ya DNS haiwezi kufikiwa"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ina muunganisho unaofikia huduma chache."</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Gusa ili uunganishe tu"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Sasa inatumia <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Kifaa hutumia <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wakati <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> haina intaneti. Huenda ukalipishwa."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Imebadilisha mtandao kutoka <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na sasa inatumia <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"aina ya mtandao isiyojulikana"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"data ya simu"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethaneti"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ta/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ta/strings.xml
new file mode 100644
index 0000000..12604cb
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ta/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"வைஃபை நெட்வொர்க்கில் உள்நுழையவும்"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"நெட்வொர்க்கில் உள்நுழையவும்"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> நெட்வொர்க்கிற்கு இணைய அணுகல் இல்லை"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"விருப்பங்களுக்கு, தட்டவும்"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"மொபைல் நெட்வொர்க்கிற்கு இணைய அணுகல் இல்லை"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"நெட்வொர்க்கிற்கு இணைய அணுகல் இல்லை"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"தனிப்பட்ட DNS சேவையகத்தை அணுக இயலாது"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> வரம்பிற்கு உட்பட்ட இணைப்புநிலையைக் கொண்டுள்ளது"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"எப்படியேனும் இணைப்பதற்குத் தட்டவும்"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>க்கு மாற்றப்பட்டது"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> நெட்வொர்க்கில் இண்டர்நெட் அணுகல் இல்லாததால், சாதனமானது <xliff:g id="NEW_NETWORK">%1$s</xliff:g> நெட்வொர்க்கைப் பயன்படுத்துகிறது. கட்டணங்கள் விதிக்கப்படலாம்."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> இலிருந்து <xliff:g id="NEW_NETWORK">%2$s</xliff:g>க்கு மாற்றப்பட்டது"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"தெரியாத நெட்வொர்க் வகை"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"மொபைல் டேட்டா"</item>
+    <item msgid="5520925862115353992">"வைஃபை"</item>
+    <item msgid="1055487873974272842">"புளூடூத்"</item>
+    <item msgid="1616528372438698248">"ஈத்தர்நெட்"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-te/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-te/strings.xml
new file mode 100644
index 0000000..84a8640
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-te/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi నెట్‌వర్క్‌కి సైన్ ఇన్ చేయండి"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"నెట్‌వర్క్‌కి సైన్ ఇన్ చేయండి"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>కి ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ఎంపికల కోసం నొక్కండి"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"మొబైల్ నెట్‌వర్క్‌కు ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"నెట్‌వర్క్‌కు ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ప్రైవేట్ DNS సర్వర్‌ను యాక్సెస్ చేయడం సాధ్యపడదు"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> పరిమిత కనెక్టివిటీని కలిగి ఉంది"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"ఏదేమైనా కనెక్ట్ చేయడానికి నొక్కండి"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>కి మార్చబడింది"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"పరికరం <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>కి ఇంటర్నెట్ యాక్సెస్ లేనప్పుడు <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ని ఉపయోగిస్తుంది. ఛార్జీలు వర్తించవచ్చు."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> నుండి <xliff:g id="NEW_NETWORK">%2$s</xliff:g>కి మార్చబడింది"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"తెలియని నెట్‌వర్క్ రకం"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"మొబైల్ డేటా"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"బ్లూటూత్"</item>
+    <item msgid="1616528372438698248">"ఈథర్‌నెట్"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-th/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-th/strings.xml
new file mode 100644
index 0000000..1616e51
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-th/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"ลงชื่อเข้าใช้เครือข่าย WiFi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"ลงชื่อเข้าใช้เครือข่าย"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> เข้าถึงอินเทอร์เน็ตไม่ได้"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"แตะเพื่อดูตัวเลือก"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"เครือข่ายมือถือไม่มีการเข้าถึงอินเทอร์เน็ต"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"เครือข่ายไม่มีการเข้าถึงอินเทอร์เน็ต"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"เข้าถึงเซิร์ฟเวอร์ DNS ไม่ได้"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> มีการเชื่อมต่อจำกัด"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"แตะเพื่อเชื่อมต่อ"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"เปลี่ยนเป็น <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"อุปกรณ์จะใช้ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> เมื่อ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> เข้าถึงอินเทอร์เน็ตไม่ได้ โดยอาจมีค่าบริการ"</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"เปลี่ยนจาก <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> เป็น <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ประเภทเครือข่ายที่ไม่รู้จัก"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"เน็ตมือถือ"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"บลูทูธ"</item>
+    <item msgid="1616528372438698248">"อีเทอร์เน็ต"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-tl/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-tl/strings.xml
new file mode 100644
index 0000000..3bf1ce4
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-tl/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Mag-sign in sa Wi-Fi network"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Mag-sign in sa network"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"Walang access sa internet ang <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"I-tap para sa mga opsyon"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Walang access sa internet ang mobile network"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Walang access sa internet ang network"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Hindi ma-access ang pribadong DNS server"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"Limitado ang koneksyon ng <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"I-tap para kumonekta pa rin"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Lumipat sa <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Ginagamit ng device ang <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kapag walang access sa internet ang <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>. Maaaring may mga malapat na singilin."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Lumipat sa <xliff:g id="NEW_NETWORK">%2$s</xliff:g> mula sa <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"isang hindi kilalang uri ng network"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobile data"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-tr/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-tr/strings.xml
new file mode 100644
index 0000000..5c326e5
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-tr/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Kablosuz ağda oturum açın"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Ağda oturum açın"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ağının internet bağlantısı yok"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Seçenekler için dokunun"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobil ağın internet bağlantısı yok"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Ağın internet bağlantısı yok"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Gizli DNS sunucusuna erişilemiyor"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> sınırlı bağlantıya sahip"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Yine de bağlanmak için dokunun"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ağına geçildi"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ağının internet erişimi olmadığında cihaz <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ağını kullanır. Bunun için ödeme alınabilir."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ağından <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ağına geçildi"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"bilinmeyen ağ türü"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobil veri"</item>
+    <item msgid="5520925862115353992">"Kablosuz"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-uk/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-uk/strings.xml
new file mode 100644
index 0000000..d1382da
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-uk/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Вхід у мережу Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Вхід у мережу"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"Мережа <xliff:g id="NETWORK_SSID">%1$s</xliff:g> не має доступу до Інтернету"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Торкніться, щоб відкрити опції"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Мобільна мережа не має доступу до Інтернету"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Мережа не має доступу до Інтернету"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Немає доступу до приватного DNS-сервера"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"Підключення до мережі <xliff:g id="NETWORK_SSID">%1$s</xliff:g> обмежено"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Натисніть, щоб усе одно підключитися"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Пристрій перейшов на мережу <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Коли мережа <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> не має доступу до Інтернету, використовується <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Може стягуватися плата."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Пристрій перейшов з мережі <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на мережу <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"невідомий тип мережі"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"мобільне передавання даних"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"Мережа VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ur/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ur/strings.xml
new file mode 100644
index 0000000..3c031ad
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ur/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"‏Wi-Fi نیٹ ورک میں سائن ان کریں"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"نیٹ ورک میں سائن ان کریں"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> کو انٹرنیٹ تک رسائی حاصل نہیں ہے"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"اختیارات کیلئے تھپتھپائیں"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"موبائل نیٹ ورک کو انٹرنیٹ تک رسائی حاصل نہیں ہے"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"نیٹ ورک کو انٹرنیٹ تک رسائی حاصل نہیں ہے"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"‏نجی DNS سرور تک رسائی حاصل نہیں کی جا سکی"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> کی کنیکٹوٹی محدود ہے"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"بہر حال منسلک کرنے کے لیے تھپتھپائیں"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> پر سوئچ ہو گیا"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"جب <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> کو انٹرنیٹ تک رسائی نہیں ہوتی ہے تو آلہ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> کا استعمال کرتا ہے۔ چارجز لاگو ہو سکتے ہیں۔"</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> سے <xliff:g id="NEW_NETWORK">%2$s</xliff:g> پر سوئچ ہو گیا"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"نیٹ ورک کی نامعلوم قسم"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"موبائل ڈیٹا"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"بلوٹوتھ"</item>
+    <item msgid="1616528372438698248">"ایتھرنیٹ"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-uz/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-uz/strings.xml
new file mode 100644
index 0000000..7518db3
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-uz/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi tarmoqqa kirish"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Tarmoqqa kirish"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nomli tarmoqda internetga ruxsati yoʻq"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Variantlarni ko‘rsatish uchun bosing"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mobil tarmoq internetga ulanmagan"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Tarmoq internetga ulanmagan"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Xususiy DNS server ishlamayapti"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nomli tarmoqda aloqa cheklangan"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Baribir ulash uchun bosing"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Yangi ulanish: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Agar <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> tarmoqda internet uzilsa, qurilma <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ga ulanadi. Sarflangan trafik uchun haq olinishi mumkin."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> tarmog‘idan <xliff:g id="NEW_NETWORK">%2$s</xliff:g> tarmog‘iga o‘tildi"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"noma’lum tarmoq turi"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"mobil internet"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-vi/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-vi/strings.xml
new file mode 100644
index 0000000..d2284d4
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-vi/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Đăng nhập vào mạng Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Đăng nhập vào mạng"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> không có quyền truy cập Internet"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Nhấn để biết tùy chọn"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Mạng di động không có quyền truy cập Internet"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Mạng không có quyền truy cập Internet"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Không thể truy cập máy chủ DNS riêng tư"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> có khả năng kết nối giới hạn"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Nhấn để tiếp tục kết nối"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Đã chuyển sang <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Thiết bị sử dụng <xliff:g id="NEW_NETWORK">%1$s</xliff:g> khi <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> không có quyền truy cập Internet. Bạn có thể phải trả phí."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Đã chuyển từ <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> sang <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"loại mạng không xác định"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"dữ liệu di động"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"Bluetooth"</item>
+    <item msgid="1616528372438698248">"Ethernet"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-zh-rCN/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..813482b
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-zh-rCN/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"登录到WLAN网络"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"登录到网络"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 无法访问互联网"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"点按即可查看相关选项"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"此移动网络无法访问互联网"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"此网络无法访问互联网"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"无法访问私人 DNS 服务器"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 的连接受限"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"点按即可继续连接"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"已切换至<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"设备会在<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>无法访问互联网时使用<xliff:g id="NEW_NETWORK">%1$s</xliff:g>(可能需要支付相应的费用)。"</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"已从<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>切换至<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"未知网络类型"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"移动数据"</item>
+    <item msgid="5520925862115353992">"WLAN"</item>
+    <item msgid="1055487873974272842">"蓝牙"</item>
+    <item msgid="1616528372438698248">"以太网"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-zh-rHK/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..676404f
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-zh-rHK/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"登入 Wi-Fi 網絡"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"登入網絡"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>未有連接至互聯網"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"輕按即可查看選項"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"流動網絡並未連接互聯網"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"網絡並未連接互聯網"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"無法存取私人 DNS 伺服器"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>連線受限"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"仍要輕按以連結至此網絡"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"已切換至<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"裝置會在 <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> 無法連線至互聯網時使用<xliff:g id="NEW_NETWORK">%1$s</xliff:g> (可能需要支付相關費用)。"</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"已從<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>切換至<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"不明網絡類型"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"流動數據"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"藍牙"</item>
+    <item msgid="1616528372438698248">"以太網"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-zh-rTW/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..f355138
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-zh-rTW/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"登入 Wi-Fi 網路"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"登入網路"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 沒有網際網路連線"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"輕觸即可查看選項"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"這個行動網路沒有網際網路連線"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"這個網路沒有網際網路連線"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"無法存取私人 DNS 伺服器"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 的連線能力受限"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"輕觸即可繼續連線"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"已切換至<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"裝置會在無法連上「<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>」時切換至「<xliff:g id="NEW_NETWORK">%1$s</xliff:g>」(可能需要支付相關費用)。"</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"已從 <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> 切換至<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"不明的網路類型"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"行動數據"</item>
+    <item msgid="5520925862115353992">"Wi-Fi"</item>
+    <item msgid="1055487873974272842">"藍牙"</item>
+    <item msgid="1616528372438698248">"乙太網路"</item>
+    <item msgid="9177085807664964627">"VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-zu/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-zu/strings.xml
new file mode 100644
index 0000000..55fefb7
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-zu/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Ngena ngemvume kunethiwekhi ye-Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="1520342291829283114">"Ngena ngemvume kunethiwekhi"</string>
+    <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
+    <string name="wifi_no_internet" msgid="1386911698276448061">"I-<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ayinakho ukufinyelela kwe-inthanethi"</string>
+    <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Thepha ukuze uthole izinketho"</string>
+    <string name="mobile_no_internet" msgid="4014455157529909781">"Inethiwekhi yeselula ayinakho ukufinyelela kwe-inthanethi"</string>
+    <string name="other_networks_no_internet" msgid="6698711684200067033">"Inethiwekhi ayinakho ukufinyelela kwenethiwekhi"</string>
+    <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Iseva eyimfihlo ye-DNS ayikwazi ukufinyelelwa"</string>
+    <string name="network_partial_connectivity" msgid="4791024923851432291">"I-<xliff:g id="NETWORK_SSID">%1$s</xliff:g> inokuxhumeka okukhawulelwe"</string>
+    <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Thepha ukuze uxhume noma kunjalo"</string>
+    <string name="network_switch_metered" msgid="1531869544142283384">"Kushintshelwe ku-<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+    <string name="network_switch_metered_detail" msgid="1358296010128405906">"Idivayisi isebenzisa i-<xliff:g id="NEW_NETWORK">%1$s</xliff:g> uma i-<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> inganakho ukufinyelela kwe-inthanethi. Kungasebenza izindleko."</string>
+    <string name="network_switch_metered_toast" msgid="501662047275723743">"Kushintshelewe kusuka ku-<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> kuya ku-<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+    <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"uhlobo olungaziwa lwenethiwekhi"</string>
+  <string-array name="network_switch_type_name">
+    <item msgid="2255670471736226365">"idatha yeselula"</item>
+    <item msgid="5520925862115353992">"I-Wi-Fi"</item>
+    <item msgid="1055487873974272842">"I-Bluetooth"</item>
+    <item msgid="1616528372438698248">"I-Ethernet"</item>
+    <item msgid="9177085807664964627">"I-VPN"</item>
+  </string-array>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml
new file mode 100644
index 0000000..71674e4
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<!-- Configuration values for ConnectivityService
+     DO NOT EDIT THIS FILE for specific device configuration; instead, use a Runtime Resources
+     Overlay package following the overlayable.xml configuration in the same directory:
+     https://source.android.com/devices/architecture/rros -->
+<resources>
+
+    <!-- Configuration hook for the URL returned by ConnectivityManager#getCaptivePortalServerUrl.
+         If empty, the returned value is controlled by Settings.Global.CAPTIVE_PORTAL_HTTP_URL,
+         and if that value is empty, the framework will use a hard-coded default.
+         This is *NOT* a URL that will always be used by the system network validation to detect
+         captive portals: NetworkMonitor may use different strategies and will not necessarily use
+         this URL. NetworkMonitor behaviour should be configured with NetworkStack resource overlays
+         instead. -->
+    <!--suppress CheckTagEmptyBody -->
+    <string translatable="false" name="config_networkCaptivePortalServerUrl"></string>
+
+    <!-- The maximum duration (in milliseconds) we expect a network transition to take -->
+    <integer name="config_networkTransitionTimeout">60000</integer>
+
+    <!-- Configuration of network interfaces that support WakeOnLAN -->
+    <string-array translatable="false" name="config_wakeonlan_supported_interfaces">
+        <!--
+        <item>wlan0</item>
+        <item>eth0</item>
+        -->
+    </string-array>
+
+    <string-array translatable="false" name="config_legacy_networktype_restore_timers">
+        <item>2,60000</item><!-- mobile_mms -->
+        <item>3,60000</item><!-- mobile_supl -->
+        <item>4,60000</item><!-- mobile_dun -->
+        <item>5,60000</item><!-- mobile_hipri -->
+        <item>10,60000</item><!-- mobile_fota -->
+        <item>11,60000</item><!-- mobile_ims -->
+        <item>12,60000</item><!-- mobile_cbs -->
+    </string-array>
+
+    <!-- Whether the APF Filter in the device should filter out IEEE 802.3 Frames
+         Those frames are identified by the field Eth-type having values
+         less than 0x600 -->
+    <bool translatable="false" name="config_apfDrop802_3Frames">true</bool>
+
+    <!-- An array of Denylisted EtherType, packets with EtherTypes within this array
+         will be dropped
+         TODO: need to put proper values, these are for testing purposes only -->
+    <integer-array translatable="false" name="config_apfEthTypeDenyList">
+        <item>0x88A2</item>
+        <item>0x88A4</item>
+        <item>0x88B8</item>
+        <item>0x88CD</item>
+        <item>0x88E3</item>
+    </integer-array>
+
+    <!-- Default supported concurrent socket keepalive slots per transport type, used by
+         ConnectivityManager.createSocketKeepalive() for calculating the number of keepalive
+         offload slots that should be reserved for privileged access. This string array should be
+         overridden by the device to present the capability of creating socket keepalives. -->
+    <!-- An Array of "[NetworkCapabilities.TRANSPORT_*],[supported keepalives] -->
+    <string-array translatable="false" name="config_networkSupportedKeepaliveCount">
+        <item>0,1</item>
+        <item>1,3</item>
+    </string-array>
+
+
+    <!-- Default value for ConnectivityManager.getMultipathPreference() on metered networks. Actual
+         device behaviour is controlled by the metered multipath preference in
+         ConnectivitySettingsManager. This is the default value of that setting. -->
+    <integer translatable="false" name="config_networkMeteredMultipathPreference">0</integer>
+
+    <!-- Whether the device should automatically switch away from Wi-Fi networks that lose
+         Internet access. Actual device behaviour is controlled by
+         Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. -->
+    <integer translatable="false" name="config_networkAvoidBadWifi">1</integer>
+
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml
new file mode 100644
index 0000000..25e19ce
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!-- 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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <overlayable name="ServiceConnectivityResourcesConfig">
+        <policy type="product|system|vendor">
+            <!-- Configuration values for ConnectivityService -->
+            <item type="array" name="config_legacy_networktype_restore_timers"/>
+            <item type="string" name="config_networkCaptivePortalServerUrl"/>
+            <item type="integer" name="config_networkTransitionTimeout"/>
+            <item type="array" name="config_wakeonlan_supported_interfaces"/>
+            <item type="bool" name="config_apfDrop802_3Frames"/>
+            <item type="array" name="config_apfEthTypeDenyList"/>
+            <item type="integer" name="config_networkMeteredMultipathPreference"/>
+            <item type="array" name="config_networkSupportedKeepaliveCount"/>
+            <item type="integer" name="config_networkAvoidBadWifi"/>
+
+        </policy>
+    </overlayable>
+</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/strings.xml
new file mode 100644
index 0000000..b2fa5f5
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/strings.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- The System Connectivity Resources package is an internal system package that provides
+         configuration values for system networking that were pre-configured in the device. This
+         is the name of the package to display in the list of system apps. [CHAR LIMIT=40] -->
+    <string name="connectivityResourcesAppLabel">System Connectivity Resources</string>
+
+    <!-- A notification is shown when a wifi captive portal network is detected.  This is the notification's title. -->
+    <string name="wifi_available_sign_in">Sign in to Wi-Fi network</string>
+
+    <!-- A notification is shown when a captive portal network is detected.  This is the notification's title. -->
+    <string name="network_available_sign_in">Sign in to network</string>
+
+    <!-- A notification is shown when a captive portal network is detected.  This is the notification's message. -->
+    <string name="network_available_sign_in_detailed"><xliff:g id="network_ssid">%1$s</xliff:g></string>
+
+    <!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's title. -->
+    <string name="wifi_no_internet"><xliff:g id="network_ssid" example="GoogleGuest">%1$s</xliff:g> has no internet access</string>
+
+    <!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's message. -->
+    <string name="wifi_no_internet_detailed">Tap for options</string>
+
+    <!-- A notification is shown when the user connects to a mobile network without internet access. This is the notification's title. -->
+    <string name="mobile_no_internet">Mobile network has no internet access</string>
+
+    <!-- A notification is shown when the user connects to a non-mobile and non-wifi network without internet access. This is the notification's title. -->
+    <string name="other_networks_no_internet">Network has no internet access</string>
+
+    <!-- A notification is shown when connected network without internet due to private dns validation failed. This is the notification's message. [CHAR LIMIT=NONE] -->
+    <string name="private_dns_broken_detailed">Private DNS server cannot be accessed</string>
+
+    <!-- A notification is shown when the user connects to a network that doesn't have access to some services (e.g. Push notifications may not work). This is the notification's title. [CHAR LIMIT=50] -->
+    <string name="network_partial_connectivity"><xliff:g id="network_ssid" example="GoogleGuest">%1$s</xliff:g> has limited connectivity</string>
+
+    <!-- A notification is shown when the user connects to a network that doesn't have access to some services (e.g. Push notifications may not work). This is the notification's message. [CHAR LIMIT=50] -->
+    <string name="network_partial_connectivity_detailed">Tap to connect anyway</string>
+
+    <!-- A notification might be shown if the device switches to another network type (e.g., mobile data) because it detects that the network it was using (e.g., Wi-Fi) has lost Internet connectivity. This is the notification's title. %1$s is the network type that the device switched to, e.g., cellular data. It is one of the strings in the network_switch_type_name array. -->
+    <string name="network_switch_metered">Switched to <xliff:g id="network_type">%1$s</xliff:g></string>
+
+    <!-- A notification might be shown if the device switches to another network type (e.g., mobile data) because it detects that the network it was using (e.g., Wi-Fi) has lost Internet connectivity. This is the notification's message. %1$s is the network that the device switched to, e.g., cellular data. %2$s is the network type the device switched from, e.g., Wi-Fi. Both are strings in the network_switch_type_name array. -->
+    <string name="network_switch_metered_detail">Device uses <xliff:g id="new_network">%1$s</xliff:g> when <xliff:g id="previous_network">%2$s</xliff:g> has no internet access. Charges may apply.</string>
+
+    <!-- A toast might be shown if the device switches to another network type (e.g., mobile data) because it detects that the network it was using (e.g., Wi-Fi) has lost Internet connectivity. This is the text of the toast. %1$s is the network that the device switched from, e.g., Wi-Fi. %2$s is the network type the device switched from, e.g., cellular data. Both are strings in the network_switch_type_name array. -->
+    <string name="network_switch_metered_toast">Switched from <xliff:g id="previous_network">%1$s</xliff:g> to <xliff:g id="new_network">%2$s</xliff:g></string>
+
+    <!-- Network type names used in the network_switch_metered and network_switch_metered_detail strings. These must be kept in the sync with the values NetworkCapabilities.TRANSPORT_xxx values, and in the same order. -->
+    <string-array name="network_switch_type_name">
+        <item>mobile data</item>
+        <item>Wi-Fi</item>
+        <item>Bluetooth</item>
+        <item>Ethernet</item>
+        <item>VPN</item>
+    </string-array>
+
+    <!-- Network type name displayed if one of the types is not found in network_switch_type_name. -->
+    <string name="network_switch_type_name_unknown">an unknown network type</string>
+
+</resources>
diff --git a/packages/Connectivity/service/jarjar-rules.txt b/packages/Connectivity/service/jarjar-rules.txt
index d8205bf..5caa11b 100644
--- a/packages/Connectivity/service/jarjar-rules.txt
+++ b/packages/Connectivity/service/jarjar-rules.txt
@@ -1,2 +1,17 @@
+rule android.sysprop.** com.android.connectivity.sysprop.@1
 rule com.android.net.module.util.** com.android.connectivity.net-utils.@1
-rule com.android.modules.utils.** com.android.connectivity.modules-utils.@1
\ No newline at end of file
+rule com.android.modules.utils.** com.android.connectivity.modules-utils.@1
+
+# internal util classes
+# Exclude AsyncChannel. TODO: remove AsyncChannel usage in ConnectivityService
+rule com.android.internal.util.AsyncChannel* @0
+# Exclude LocationPermissionChecker. This is going to be moved to libs/net
+rule com.android.internal.util.LocationPermissionChecker* @0
+rule android.util.LocalLog* com.android.connectivity.util.LocalLog@1
+# android.util.IndentingPrintWriter* should use a different package name from
+# the one in com.android.internal.util
+rule android.util.IndentingPrintWriter* android.connectivity.util.IndentingPrintWriter@1
+rule com.android.internal.util.** com.android.connectivity.util.@1
+
+rule com.android.internal.messages.** com.android.connectivity.messages.@1
+rule com.google.protobuf.** com.android.connectivity.protobuf.@1
diff --git a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl b/packages/Connectivity/service/proto/connectivityproto.proto
similarity index 69%
copy from packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
copy to packages/Connectivity/service/proto/connectivityproto.proto
index 7979afc..a992d7c 100644
--- a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
+++ b/packages/Connectivity/service/proto/connectivityproto.proto
@@ -1,12 +1,11 @@
-/**
- *
+/*
  * 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
+ *      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,
@@ -15,9 +14,7 @@
  * limitations under the License.
  */
 
-package android.net;
+syntax = "proto2";
 
-/** @hide */
-oneway interface IOnSetOemNetworkPreferenceListener {
-    void onComplete();
-}
+// Connectivity protos can be created in this directory. Note this file must be included before
+// building system-messages-proto, otherwise it will not build by itself.
diff --git a/packages/CtsShim/apk/arm/CtsShim.apk b/packages/CtsShim/apk/arm/CtsShim.apk
index 180dfb6..784a747 100644
--- a/packages/CtsShim/apk/arm/CtsShim.apk
+++ b/packages/CtsShim/apk/arm/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/arm/CtsShimPriv.apk b/packages/CtsShim/apk/arm/CtsShimPriv.apk
index d87ea7f..5b7bda4 100644
--- a/packages/CtsShim/apk/arm/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/arm/CtsShimPriv.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShim.apk b/packages/CtsShim/apk/x86/CtsShim.apk
index 180dfb6..784a747 100644
--- a/packages/CtsShim/apk/x86/CtsShim.apk
+++ b/packages/CtsShim/apk/x86/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShimPriv.apk b/packages/CtsShim/apk/x86/CtsShimPriv.apk
index 3124651..780cb8a 100644
--- a/packages/CtsShim/apk/x86/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/x86/CtsShimPriv.apk
Binary files differ
diff --git a/packages/CtsShim/build/shim/AndroidManifest.xml b/packages/CtsShim/build/shim/AndroidManifest.xml
index 3e546f1e..58e8522 100644
--- a/packages/CtsShim/build/shim/AndroidManifest.xml
+++ b/packages/CtsShim/build/shim/AndroidManifest.xml
@@ -52,6 +52,9 @@
             </intent-filter>
         </activity>
 
+        <!-- The stub shared library for package visibility test -->
+        <library android:name="com.android.cts.ctsshim.shared_library" />
+
     </application>
 </manifest>
 
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index c1dca5d..16a946d 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -138,9 +138,6 @@
     private long mCurrentPartitionSize;
     private long mCurrentPartitionInstalledSize;
 
-    private boolean mJustCancelledByUser;
-    private boolean mKeepNotification;
-
     // This is for testing only now
     private boolean mEnableWhenCompleted;
 
@@ -174,11 +171,6 @@
         if (cache != null) {
             cache.flush();
         }
-
-        if (!mKeepNotification) {
-            // Cancel the persistent notification.
-            mNM.cancel(NOTIFICATION_ID);
-        }
     }
 
     @Override
@@ -231,9 +223,11 @@
             return;
         }
 
+        boolean removeNotification = false;
         switch (result) {
             case RESULT_CANCELLED:
                 postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null);
+                removeNotification = true;
                 break;
 
             case RESULT_ERROR_IO:
@@ -251,7 +245,7 @@
         }
 
         // if it's not successful, reset the task and stop self.
-        resetTaskAndStop();
+        resetTaskAndStop(removeNotification);
     }
 
     private void executeInstallCommand(Intent intent) {
@@ -302,12 +296,12 @@
             return;
         }
 
-        stopForeground(true);
-        mJustCancelledByUser = true;
-
         if (mInstallTask.cancel(false)) {
-            // Will stopSelf() in onResult()
+            // onResult() would call resetTaskAndStop() upon task completion.
             Log.d(TAG, "Cancel request filed successfully");
+            // Dismiss the notification as soon as possible as DynamicSystemManager.remove() may
+            // block.
+            stopForeground(STOP_FOREGROUND_REMOVE);
         } else {
             Log.e(TAG, "Trying to cancel installation while it's already completed.");
         }
@@ -322,8 +316,7 @@
         if (!isDynamicSystemInstalled() && (getStatus() != STATUS_READY)) {
             Log.e(TAG, "Trying to discard AOT while there is no complete installation");
             // Stop foreground state and dismiss stale notification.
-            stopForeground(STOP_FOREGROUND_REMOVE);
-            resetTaskAndStop();
+            resetTaskAndStop(true);
             return;
         }
 
@@ -331,8 +324,8 @@
                 getString(R.string.toast_dynsystem_discarded),
                 Toast.LENGTH_LONG).show();
 
-        resetTaskAndStop();
         postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null);
+        resetTaskAndStop(true);
 
         mDynSystem.remove();
     }
@@ -412,12 +405,13 @@
     }
 
     private void resetTaskAndStop() {
-        mInstallTask = null;
+        resetTaskAndStop(/* removeNotification= */ false);
+    }
 
-        new Handler().postDelayed(() -> {
-            stopForeground(STOP_FOREGROUND_DETACH);
-            stopSelf();
-        }, 50);
+    private void resetTaskAndStop(boolean removeNotification) {
+        mInstallTask = null;
+        stopForeground(removeNotification ? STOP_FOREGROUND_REMOVE : STOP_FOREGROUND_DETACH);
+        stopSelf();
     }
 
     private void prepareNotification() {
@@ -525,7 +519,7 @@
     private void postStatus(int status, int cause, Throwable detail) {
         String statusString;
         String causeString;
-        mKeepNotification = false;
+        boolean notifyOnNotificationBar = true;
 
         switch (status) {
             case STATUS_NOT_STARTED:
@@ -551,18 +545,16 @@
                 break;
             case CAUSE_INSTALL_CANCELLED:
                 causeString = "INSTALL_CANCELLED";
+                notifyOnNotificationBar = false;
                 break;
             case CAUSE_ERROR_IO:
                 causeString = "ERROR_IO";
-                mKeepNotification = true;
                 break;
             case CAUSE_ERROR_INVALID_URL:
                 causeString = "ERROR_INVALID_URL";
-                mKeepNotification = true;
                 break;
             case CAUSE_ERROR_EXCEPTION:
                 causeString = "ERROR_EXCEPTION";
-                mKeepNotification = true;
                 break;
             default:
                 causeString = "CAUSE_NOT_SPECIFIED";
@@ -571,16 +563,6 @@
 
         Log.d(TAG, "status=" + statusString + ", cause=" + causeString + ", detail=" + detail);
 
-        boolean notifyOnNotificationBar = true;
-
-        if (status == STATUS_NOT_STARTED
-                && cause == CAUSE_INSTALL_CANCELLED
-                && mJustCancelledByUser) {
-            // if task is cancelled by user, do not notify them
-            notifyOnNotificationBar = false;
-            mJustCancelledByUser = false;
-        }
-
         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 4ef5e2b..59ea9f0 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -320,7 +320,7 @@
         }
     }
 
-    private void installScratch() throws IOException, InterruptedException {
+    private void installScratch() throws IOException {
         final long scratchSize = mDynSystem.suggestScratchSize();
         Thread thread = new Thread() {
             @Override
@@ -347,7 +347,11 @@
                 publishProgress(progress);
             }
 
-            Thread.sleep(100);
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+                // Ignore the error.
+            }
         }
 
         if (mInstallationSession == null) {
@@ -361,7 +365,7 @@
         }
     }
 
-    private void installUserdata() throws IOException, InterruptedException {
+    private void installUserdata() throws IOException {
         Thread thread = new Thread(() -> {
             mInstallationSession = mDynSystem.createPartition("userdata", mUserdataSize, false);
         });
@@ -383,7 +387,11 @@
                 publishProgress(progress);
             }
 
-            Thread.sleep(100);
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+                // Ignore the error.
+            }
         }
 
         if (mInstallationSession == null) {
@@ -397,8 +405,7 @@
         }
     }
 
-    private void installImages()
-            throws IOException, InterruptedException, ImageValidationException {
+    private void installImages() throws IOException, ImageValidationException {
         if (mStream != null) {
             if (mIsZip) {
                 installStreamingZipUpdate();
@@ -410,14 +417,12 @@
         }
     }
 
-    private void installStreamingGzUpdate()
-            throws IOException, InterruptedException, ImageValidationException {
+    private void installStreamingGzUpdate() throws IOException, ImageValidationException {
         Log.d(TAG, "To install a streaming GZ update");
         installImage("system", mSystemSize, new GZIPInputStream(mStream));
     }
 
-    private void installStreamingZipUpdate()
-            throws IOException, InterruptedException, ImageValidationException {
+    private void installStreamingZipUpdate() throws IOException, ImageValidationException {
         Log.d(TAG, "To install a streaming ZIP update");
 
         ZipInputStream zis = new ZipInputStream(mStream);
@@ -432,8 +437,7 @@
         }
     }
 
-    private void installLocalZipUpdate()
-            throws IOException, InterruptedException, ImageValidationException {
+    private void installLocalZipUpdate() throws IOException, ImageValidationException {
         Log.d(TAG, "To install a local ZIP update");
 
         Enumeration<? extends ZipEntry> entries = mZipFile.entries();
@@ -449,7 +453,7 @@
     }
 
     private boolean installImageFromAnEntry(ZipEntry entry, InputStream is)
-            throws IOException, InterruptedException, ImageValidationException {
+            throws IOException, ImageValidationException {
         String name = entry.getName();
 
         Log.d(TAG, "ZipEntry: " + name);
@@ -473,7 +477,7 @@
     }
 
     private void installImage(String partitionName, long uncompressedSize, InputStream is)
-            throws IOException, InterruptedException, ImageValidationException {
+            throws IOException, ImageValidationException {
 
         SparseInputStream sis = new SparseInputStream(new BufferedInputStream(is));
 
@@ -504,7 +508,11 @@
                 return;
             }
 
-            Thread.sleep(100);
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+                // Ignore the error.
+            }
         }
 
         if (mInstallationSession == null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index ad459a4..40a457f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -22,7 +22,6 @@
 import android.graphics.drawable.Drawable;
 import android.location.LocationManager;
 import android.media.AudioManager;
-import android.net.ConnectivityManager;
 import android.net.TetheringManager;
 import android.os.BatteryManager;
 import android.os.SystemProperties;
@@ -33,6 +32,7 @@
 import android.telephony.AccessNetworkConstants;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
 
 import androidx.annotation.NonNull;
 import androidx.core.graphics.drawable.RoundedBitmapDrawable;
@@ -435,8 +435,7 @@
     }
 
     public static boolean isWifiOnly(Context context) {
-        return !context.getSystemService(ConnectivityManager.class)
-                .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+        return !context.getSystemService(TelephonyManager.class).isDataCapable();
     }
 
     /** Returns if the automatic storage management feature is turned on or not. **/
diff --git a/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS b/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS
index e7a20b3..c88ed8e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS
@@ -1,6 +1,7 @@
 # Default reviewers for this and subdirectories.
 andychou@google.com
 arcwang@google.com
+changbetty@google.com
 goldmanj@google.com
 qal@google.com
 wengsu@google.com
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractIpAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractIpAddressPreferenceController.java
index 3bb3a0c..7f12cc8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractIpAddressPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractIpAddressPreferenceController.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.net.ConnectivityManager;
+import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.wifi.WifiManager;
 
@@ -28,7 +29,6 @@
 import com.android.settingslib.R;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
-import java.net.InetAddress;
 import java.util.Iterator;
 
 /**
@@ -93,19 +93,19 @@
      * @return the formatted and newline-separated IP addresses, or null if none.
      */
     private static String getDefaultIpAddresses(ConnectivityManager cm) {
-        LinkProperties prop = cm.getActiveLinkProperties();
+        LinkProperties prop = cm.getLinkProperties(cm.getActiveNetwork());
         return formatIpAddresses(prop);
     }
 
     private static String formatIpAddresses(LinkProperties prop) {
         if (prop == null) return null;
-        Iterator<InetAddress> iter = prop.getAllAddresses().iterator();
+        Iterator<LinkAddress> iter = prop.getAllLinkAddresses().iterator();
         // If there are no entries, return null
         if (!iter.hasNext()) return null;
         // Concatenate all available addresses, newline separated
         StringBuilder addresses = new StringBuilder();
         while (iter.hasNext()) {
-            addresses.append(iter.next().getHostAddress());
+            addresses.append(iter.next().getAddress().getHostAddress());
             if (iter.hasNext()) addresses.append("\n");
         }
         return addresses.toString();
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index 092cbf3..60bcf37 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -16,7 +16,6 @@
 
 package com.android.settingslib.net;
 
-import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
 import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
 import static android.net.TrafficStats.MB_IN_BYTES;
@@ -59,7 +58,6 @@
             PERIOD_BUILDER, Locale.getDefault());
 
     private final Context mContext;
-    private final ConnectivityManager mConnectivityManager;
     private final INetworkStatsService mStatsService;
     private final NetworkPolicyManager mPolicyManager;
     private final NetworkStatsManager mNetworkStatsManager;
@@ -71,7 +69,6 @@
 
     public DataUsageController(Context context) {
         mContext = context;
-        mConnectivityManager = ConnectivityManager.from(context);
         mStatsService = INetworkStatsService.Stub.asInterface(
                 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
         mPolicyManager = NetworkPolicyManager.from(mContext);
@@ -236,7 +233,7 @@
 
     public boolean isMobileDataSupported() {
         // require both supported network and ready SIM
-        return mConnectivityManager.isNetworkSupported(TYPE_MOBILE)
+        return getTelephonyManager().isDataCapable()
                 && getTelephonyManager().getSimState() == SIM_STATE_READY;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
index 0bde5c0..f3b600c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
@@ -2,6 +2,7 @@
 andychou@google.com
 arcwang@google.com
 asapperstein@google.com
+changbetty@google.com
 goldmanj@google.com
 qal@google.com
 wengsu@google.com
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 6568bff..268603f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -2445,8 +2445,8 @@
                     R.bool.def_auto_time_zone); // Sync timezone to NITZ
 
             loadSetting(stmt, Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
-                    ("1".equals(SystemProperties.get("ro.kernel.qemu")) ||
-                        res.getBoolean(R.bool.def_stay_on_while_plugged_in))
+                    ("1".equals(SystemProperties.get("ro.boot.qemu"))
+                        || res.getBoolean(R.bool.def_stay_on_while_plugged_in))
                      ? 1 : 0);
 
             loadIntegerSetting(stmt, Settings.Global.WIFI_SLEEP_POLICY,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 3fed29a..b7ce2a3 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -367,6 +367,7 @@
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
     <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
     <uses-permission android:name="android.permission.BIND_RESUME_ON_REBOOT_SERVICE" />
+    <uses-permission android:name="android.permission.MANAGE_APP_HIBERNATION"/>
 
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/VpnDialogs/res/values/strings.xml b/packages/VpnDialogs/res/values/strings.xml
index 443a9bc..f971a09 100644
--- a/packages/VpnDialogs/res/values/strings.xml
+++ b/packages/VpnDialogs/res/values/strings.xml
@@ -28,6 +28,17 @@
         ]]> appears at the top of your screen when VPN is active.
     </string>
 
+    <!-- TV specific dialog message to warn about the risk of using a VPN application. [CHAR LIMIT=NONE] -->
+    <string name="warning" product="tv">
+      <xliff:g id="app">%s</xliff:g> wants to set up a VPN connection
+        that allows it to monitor network traffic. Only accept if you trust the source.
+        <![CDATA[
+        <br />
+        <br />
+        <img src="vpn_icon" />
+        ]]> appears on your screen when VPN is active.
+    </string>
+
     <!-- Dialog title for built-in VPN. [CHAR LIMIT=40]  -->
     <string name="legacy_title">VPN is connected</string>
     <!-- Label for the name of the current VPN session. [CHAR LIMIT=20] -->
diff --git a/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
index f8b9309..f0de811 100644
--- a/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
+++ b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
@@ -360,7 +360,7 @@
             try {
                 mCallback.setProxyPort(port);
             } catch (RemoteException e) {
-                Log.w(TAG, "Proxy failed to report port to PacProxyInstaller", e);
+                Log.w(TAG, "Proxy failed to report port to PacProxyService", e);
             }
         }
         mPort = port;
@@ -371,7 +371,7 @@
             try {
                 callback.setProxyPort(mPort);
             } catch (RemoteException e) {
-                Log.w(TAG, "Proxy failed to report port to PacProxyInstaller", e);
+                Log.w(TAG, "Proxy failed to report port to PacProxyService", e);
             }
         }
         mCallback = callback;
diff --git a/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java b/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java
index bdf478d..a8e2622 100644
--- a/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java
+++ b/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java
@@ -30,7 +30,7 @@
 
     private static ProxyServer server = null;
 
-    /** Keep these values up-to-date with PacProxyInstaller.java */
+    /** Keep these values up-to-date with PacProxyService.java */
     public static final String KEY_PROXY = "keyProxy";
     public static final String HOST = "localhost";
     public static final String EXCL_LIST = "";
diff --git a/services/core/Android.bp b/services/core/Android.bp
index f1ab2aa..0ac8f74 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -81,9 +81,18 @@
     out: ["services.core.protolog.json"],
 }
 
+genrule {
+    name: "statslog-art-java-gen",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --java $(out) --module art" +
+         " --javaPackage com.android.internal.art --javaClass ArtStatsLog --worksource",
+    out: ["com/android/internal/art/ArtStatsLog.java"],
+}
+
 java_library_static {
     name: "services.core.unboosted",
     srcs: [
+        ":statslog-art-java-gen",
         ":services.core.protologsrc",
         ":dumpstate_aidl",
         ":framework_native_aidl",
@@ -201,6 +210,7 @@
         "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",
diff --git a/services/core/java/com/android/server/BluetoothDeviceConfigListener.java b/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
index 2dcf82f..611a37d 100644
--- a/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
+++ b/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
@@ -17,6 +17,9 @@
 package com.android.server;
 
 import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import java.util.ArrayList;
 
 /**
  * The BluetoothDeviceConfigListener handles system device config change callback and checks
@@ -30,10 +33,12 @@
 class BluetoothDeviceConfigListener {
     private static final String TAG = "BluetoothDeviceConfigListener";
 
-    BluetoothManagerService mService;
+    private final BluetoothManagerService mService;
+    private final boolean mLogDebug;
 
-    BluetoothDeviceConfigListener(BluetoothManagerService service) {
+    BluetoothDeviceConfigListener(BluetoothManagerService service, boolean logDebug) {
         mService = service;
+        mLogDebug = logDebug;
         DeviceConfig.addOnPropertiesChangedListener(
                 DeviceConfig.NAMESPACE_BLUETOOTH,
                 (Runnable r) -> r.run(),
@@ -47,6 +52,13 @@
                     if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_BLUETOOTH)) {
                         return;
                     }
+                    if (mLogDebug) {
+                        ArrayList<String> flags = new ArrayList<>();
+                        for (String name : properties.getKeyset()) {
+                            flags.add(name + "='" + properties.getString(name, "") + "'");
+                        }
+                        Slog.d(TAG, "onPropertiesChanged: " + String.join(",", flags));
+                    }
                     boolean foundInit = false;
                     for (String name : properties.getKeyset()) {
                         if (name.startsWith("INIT_")) {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index aab0553..992ef26 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -453,6 +453,7 @@
                 if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED)
                         && state == BluetoothProfile.STATE_DISCONNECTED
                         && !mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) {
+                    Slog.i(TAG, "Device disconnected, reactivating pending flag changes");
                     onInitFlagsChanged();
                 }
             }
@@ -810,6 +811,35 @@
         return enabledProfiles;
     }
 
+    private boolean isDeviceProvisioned() {
+        return Settings.Global.getInt(mContentResolver, Settings.Global.DEVICE_PROVISIONED,
+                0) != 0;
+    }
+
+    // Monitor change of BLE scan only mode settings.
+    private void registerForProvisioningStateChange() {
+        ContentObserver contentObserver = new ContentObserver(null) {
+            @Override
+            public void onChange(boolean selfChange) {
+                if (!isDeviceProvisioned()) {
+                    if (DBG) {
+                        Slog.d(TAG, "DEVICE_PROVISIONED setting changed, but device is not "
+                                + "provisioned");
+                    }
+                    return;
+                }
+                if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED)) {
+                    Slog.i(TAG, "Device provisioned, reactivating pending flag changes");
+                    onInitFlagsChanged();
+                }
+            }
+        };
+
+        mContentResolver.registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), false,
+                contentObserver);
+    }
+
     // Monitor change of BLE scan only mode settings.
     private void registerForBleScanModeChange() {
         ContentObserver contentObserver = new ContentObserver(null) {
@@ -1375,7 +1405,8 @@
         if (mBluetoothAirplaneModeListener != null) {
             mBluetoothAirplaneModeListener.start(mBluetoothModeChangeHelper);
         }
-        mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this);
+        registerForProvisioningStateChange();
+        mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this, DBG);
     }
 
     /**
@@ -2219,12 +2250,25 @@
                     }
                     mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED);
                     if (mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) {
+                        Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by "
+                                + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS
+                                + " ms due to existing connections");
+                        mHandler.sendEmptyMessageDelayed(
+                                MESSAGE_INIT_FLAGS_CHANGED,
+                                DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS);
+                        break;
+                    }
+                    if (!isDeviceProvisioned()) {
+                        Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by "
+                                + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS
+                                +  "ms because device is not provisioned");
                         mHandler.sendEmptyMessageDelayed(
                                 MESSAGE_INIT_FLAGS_CHANGED,
                                 DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS);
                         break;
                     }
                     if (mBluetooth != null && isEnabled()) {
+                        Slog.i(TAG, "Restarting Bluetooth due to init flag change");
                         restartForReason(
                                 BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED);
                     }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 07b473c..0c42585 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -17,6 +17,10 @@
 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;
+import static android.content.pm.PackageManager.FEATURE_WIFI;
+import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_ATTEMPTED_BITMASK;
 import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_SUCCEEDED_BITMASK;
@@ -26,17 +30,35 @@
 import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_DNS_CONSECUTIVE_TIMEOUTS;
 import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS;
 import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_PACKET_FAIL_RATE;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_MASK;
+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;
+import static android.net.ConnectivityManager.TYPE_MOBILE_CBS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
+import static android.net.ConnectivityManager.TYPE_MOBILE_EMERGENCY;
+import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
+import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+import static android.net.ConnectivityManager.TYPE_MOBILE_IA;
+import static android.net.ConnectivityManager.TYPE_MOBILE_IMS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
 import static android.net.ConnectivityManager.TYPE_NONE;
+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.ConnectivityManager.TYPE_WIFI_P2P;
 import static android.net.ConnectivityManager.getNetworkTypeName;
 import static android.net.ConnectivityManager.isNetworkTypeValid;
 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;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
@@ -50,11 +72,14 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION;
+import static android.net.NetworkCapabilities.REDACT_FOR_LOCAL_MAC_ADDRESS;
+import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_TEST;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
-import static android.net.NetworkPolicyManager.RULE_NONE;
-import static android.net.NetworkPolicyManager.uidRulesToString;
+import static android.net.NetworkPolicyManager.blockedReasonsToString;
+import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST;
 import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired;
 import static android.os.Process.INVALID_UID;
 import static android.os.Process.VPN_UID;
@@ -85,6 +110,11 @@
 import android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
 import android.net.ConnectivityDiagnosticsManager.DataStallReport;
 import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.BlockedReason;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.ConnectivityManager.RestrictBackgroundStatus;
+import android.net.ConnectivityResources;
+import android.net.ConnectivitySettingsManager;
 import android.net.DataStallReportParcelable;
 import android.net.DnsResolverServiceManager;
 import android.net.ICaptivePortal;
@@ -93,10 +123,10 @@
 import android.net.IDnsResolver;
 import android.net.INetd;
 import android.net.INetworkActivityListener;
+import android.net.INetworkAgent;
 import android.net.INetworkMonitor;
 import android.net.INetworkMonitorCallbacks;
-import android.net.INetworkPolicyListener;
-import android.net.IOnSetOemNetworkPreferenceListener;
+import android.net.IOnCompleteListener;
 import android.net.IQosCallback;
 import android.net.ISocketKeepaliveCallback;
 import android.net.InetAddresses;
@@ -109,13 +139,14 @@
 import android.net.NetworkAgent;
 import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
-import android.net.NetworkConfig;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkMonitorManager;
 import android.net.NetworkPolicyManager;
+import android.net.NetworkPolicyManager.NetworkPolicyCallback;
 import android.net.NetworkProvider;
 import android.net.NetworkRequest;
+import android.net.NetworkScore;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
 import android.net.NetworkStackClient;
@@ -175,6 +206,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.sysprop.NetworkProperties;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -185,23 +217,18 @@
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 
-import com.android.connectivity.aidl.INetworkAgent;
-import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.LocationPermissionChecker;
 import com.android.internal.util.MessageUtils;
 import com.android.modules.utils.BasicShellCommandHandler;
 import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
 import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
+import com.android.net.module.util.LocationPermissionChecker;
 import com.android.net.module.util.NetworkCapabilitiesUtils;
 import com.android.net.module.util.PermissionUtils;
-import com.android.server.am.BatteryStatsService;
 import com.android.server.connectivity.AutodestructReference;
 import com.android.server.connectivity.DnsManager;
 import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
@@ -214,10 +241,9 @@
 import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
 import com.android.server.connectivity.NetworkRanker;
 import com.android.server.connectivity.PermissionMonitor;
+import com.android.server.connectivity.ProfileNetworkPreferences;
 import com.android.server.connectivity.ProxyTracker;
 import com.android.server.connectivity.QosCallbackTracker;
-import com.android.server.net.NetworkPolicyManagerInternal;
-import com.android.server.utils.PriorityDump;
 
 import libcore.io.IoUtils;
 
@@ -265,7 +291,7 @@
     /**
      * Default URL to use for {@link #getCaptivePortalServerUrl()}. This should not be changed
      * by OEMs for configuration purposes, as this value is overridden by
-     * Settings.Global.CAPTIVE_PORTAL_HTTP_URL.
+     * ConnectivitySettingsManager.CAPTIVE_PORTAL_HTTP_URL.
      * R.string.config_networkCaptivePortalServerUrl should be overridden instead for this purpose
      * (preferably via runtime resource overlays).
      */
@@ -298,7 +324,7 @@
     protected int mNascentDelayMs;
 
     // How long to delay to removal of a pending intent based request.
-    // See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
+    // See ConnectivitySettingsManager.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
     private final int mReleasePendingIntentDelayMs;
 
     private MockableSystemProperties mSystemProperties;
@@ -311,14 +337,13 @@
     private volatile boolean mLockdownEnabled;
 
     /**
-     * Stale copy of uid rules provided by NPMS. As long as they are accessed only in internal
-     * handler thread, they don't need a lock.
+     * Stale copy of uid blocked reasons provided by NPMS. As long as they are accessed only in
+     * internal handler thread, they don't need a lock.
      */
-    private SparseIntArray mUidRules = new SparseIntArray();
-    /** Flag indicating if background data is restricted. */
-    private boolean mRestrictBackground;
+    private SparseIntArray mUidBlockedReasons = new SparseIntArray();
 
     private final Context mContext;
+    private final ConnectivityResources mResources;
     // The Context is created for UserHandle.ALL.
     private final Context mUserAllContext;
     private final Dependencies mDeps;
@@ -331,7 +356,6 @@
     protected INetd mNetd;
     private NetworkStatsManager mStatsManager;
     private NetworkPolicyManager mPolicyManager;
-    private NetworkPolicyManagerInternal mPolicyManagerInternal;
     private final NetdCallback mNetdCallback;
 
     /**
@@ -346,8 +370,7 @@
     private String mCurrentTcpBufferSizes;
 
     private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(
-            new Class[] { AsyncChannel.class, ConnectivityService.class, NetworkAgent.class,
-                    NetworkAgentInfo.class });
+            new Class[] { ConnectivityService.class, NetworkAgent.class, NetworkAgentInfo.class });
 
     private enum ReapUnvalidatedNetworks {
         // Tear down networks that have no chance (e.g. even if validated) of becoming
@@ -490,16 +513,6 @@
     // Handle private DNS validation status updates.
     private static final int EVENT_PRIVATE_DNS_VALIDATION_UPDATE = 38;
 
-    /**
-     * Used to handle onUidRulesChanged event from NetworkPolicyManagerService.
-     */
-    private static final int EVENT_UID_RULES_CHANGED = 39;
-
-    /**
-     * Used to handle onRestrictBackgroundChanged event from NetworkPolicyManagerService.
-     */
-    private static final int EVENT_DATA_SAVER_CHANGED = 40;
-
      /**
       * Event for NetworkMonitor/NetworkAgentInfo to inform ConnectivityService that the network has
       * been tested.
@@ -559,8 +572,8 @@
     private static final int EVENT_SET_REQUIRE_VPN_FOR_UIDS = 47;
 
     /**
-     * used internally when setting the default networks for OemNetworkPreferences.
-     * obj = OemNetworkPreferences
+     * Used internally when setting the default networks for OemNetworkPreferences.
+     * obj = Pair<OemNetworkPreferences, listener>
      */
     private static final int EVENT_SET_OEM_NETWORK_PREFERENCE = 48;
 
@@ -570,6 +583,19 @@
     private static final int EVENT_REPORT_NETWORK_ACTIVITY = 49;
 
     /**
+     * Used internally when setting a network preference for a user profile.
+     * obj = Pair<ProfileNetworkPreference, Listener>
+     */
+    private static final int EVENT_SET_PROFILE_NETWORK_PREFERENCE = 50;
+
+    /**
+     * Event to specify that reasons for why an uid is blocked changed.
+     * arg1 = uid
+     * arg2 = blockedReasons
+     */
+    private static final int EVENT_UID_BLOCKED_REASON_CHANGED = 51;
+
+    /**
      * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
      * should be shown.
      */
@@ -607,7 +633,6 @@
     private Intent mInitialBroadcast;
 
     private PowerManager.WakeLock mNetTransitionWakeLock;
-    private int mNetTransitionWakeLockTimeout;
     private final PowerManager.WakeLock mPendingIntentWakeLock;
 
     // A helper object to track the current default HTTP proxy. ConnectivityService needs to tell
@@ -619,11 +644,8 @@
 
     private UserManager mUserManager;
 
-    private NetworkConfig[] mNetConfigs;
-    private int mNetworksDefined;
-
     // the set of network types that can only be enabled by system/sig apps
-    private List mProtectedNetworks;
+    private List<Integer> mProtectedNetworks;
 
     private Set<String> mWolSupportedInterfaces;
 
@@ -713,18 +735,63 @@
          *    They are therefore not thread-safe with respect to each other.
          *  - getNetworkForType() can be called at any time on binder threads. It is synchronized
          *    on mTypeLists to be thread-safe with respect to a concurrent remove call.
+         *  - getRestoreTimerForType(type) is also synchronized on mTypeLists.
          *  - dump is thread-safe with respect to concurrent add and remove calls.
          */
         private final ArrayList<NetworkAgentInfo> mTypeLists[];
         @NonNull
         private final ConnectivityService mService;
 
+        // Restore timers for requestNetworkForFeature (network type -> timer in ms). Types without
+        // an entry have no timer (equivalent to -1). Lazily loaded.
+        @NonNull
+        private ArrayMap<Integer, Integer> mRestoreTimers = new ArrayMap<>();
+
         LegacyTypeTracker(@NonNull ConnectivityService service) {
             mService = service;
             mTypeLists = new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1];
         }
 
-        public void addSupportedType(int type) {
+        public void loadSupportedTypes(@NonNull Context ctx, @NonNull TelephonyManager tm) {
+            final PackageManager pm = ctx.getPackageManager();
+            if (pm.hasSystemFeature(FEATURE_WIFI)) {
+                addSupportedType(TYPE_WIFI);
+            }
+            if (pm.hasSystemFeature(FEATURE_WIFI_DIRECT)) {
+                addSupportedType(TYPE_WIFI_P2P);
+            }
+            if (tm.isDataCapable()) {
+                // Telephony does not have granular support for these types: they are either all
+                // supported, or none is supported
+                addSupportedType(TYPE_MOBILE);
+                addSupportedType(TYPE_MOBILE_MMS);
+                addSupportedType(TYPE_MOBILE_SUPL);
+                addSupportedType(TYPE_MOBILE_DUN);
+                addSupportedType(TYPE_MOBILE_HIPRI);
+                addSupportedType(TYPE_MOBILE_FOTA);
+                addSupportedType(TYPE_MOBILE_IMS);
+                addSupportedType(TYPE_MOBILE_CBS);
+                addSupportedType(TYPE_MOBILE_IA);
+                addSupportedType(TYPE_MOBILE_EMERGENCY);
+            }
+            if (pm.hasSystemFeature(FEATURE_BLUETOOTH)) {
+                addSupportedType(TYPE_BLUETOOTH);
+            }
+            if (pm.hasSystemFeature(FEATURE_WATCH)) {
+                // TYPE_PROXY is only used on Wear
+                addSupportedType(TYPE_PROXY);
+            }
+            // Ethernet is often not specified in the configs, although many devices can use it via
+            // USB host adapters. Add it as long as the ethernet service is here.
+            if (ctx.getSystemService(Context.ETHERNET_SERVICE) != null) {
+                addSupportedType(TYPE_ETHERNET);
+            }
+
+            // Always add TYPE_VPN as a supported type
+            addSupportedType(TYPE_VPN);
+        }
+
+        private void addSupportedType(int type) {
             if (mTypeLists[type] != null) {
                 throw new IllegalStateException(
                         "legacy list for type " + type + "already initialized");
@@ -745,6 +812,35 @@
             return null;
         }
 
+        public int getRestoreTimerForType(int type) {
+            synchronized (mTypeLists) {
+                if (mRestoreTimers == null) {
+                    mRestoreTimers = loadRestoreTimers();
+                }
+                return mRestoreTimers.getOrDefault(type, -1);
+            }
+        }
+
+        private ArrayMap<Integer, Integer> loadRestoreTimers() {
+            final String[] configs = mService.mResources.get().getStringArray(
+                    com.android.connectivity.resources.R.array
+                            .config_legacy_networktype_restore_timers);
+            final ArrayMap<Integer, Integer> ret = new ArrayMap<>(configs.length);
+            for (final String config : configs) {
+                final String[] splits = TextUtils.split(config, ",");
+                if (splits.length != 2) {
+                    logwtf("Invalid restore timer token count: " + config);
+                    continue;
+                }
+                try {
+                    ret.put(Integer.parseInt(splits[0]), Integer.parseInt(splits[1]));
+                } catch (NumberFormatException e) {
+                    logwtf("Invalid restore timer number format: " + config, e);
+                }
+            }
+            return ret;
+        }
+
         private void maybeLogBroadcast(NetworkAgentInfo nai, DetailedState state, int type,
                 boolean isDefaultNetwork) {
             if (DBG) {
@@ -884,27 +980,59 @@
     }
     private final LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker(this);
 
+    final LocalPriorityDump mPriorityDumper = new LocalPriorityDump();
     /**
      * Helper class which parses out priority arguments and dumps sections according to their
      * priority. If priority arguments are omitted, function calls the legacy dump command.
      */
-    private final PriorityDump.PriorityDumper mPriorityDumper = new PriorityDump.PriorityDumper() {
-        @Override
-        public void dumpHigh(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
-            doDump(fd, pw, new String[] {DIAG_ARG}, asProto);
-            doDump(fd, pw, new String[] {SHORT_ARG}, asProto);
+    private class LocalPriorityDump {
+        private static final String PRIORITY_ARG = "--dump-priority";
+        private static final String PRIORITY_ARG_HIGH = "HIGH";
+        private static final String PRIORITY_ARG_NORMAL = "NORMAL";
+
+        LocalPriorityDump() {}
+
+        private void dumpHigh(FileDescriptor fd, PrintWriter pw) {
+            doDump(fd, pw, new String[] {DIAG_ARG});
+            doDump(fd, pw, new String[] {SHORT_ARG});
         }
 
-        @Override
-        public void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
-            doDump(fd, pw, args, asProto);
+        private void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args) {
+            doDump(fd, pw, args);
         }
 
-        @Override
-        public void dump(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
-           doDump(fd, pw, args, asProto);
+        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (args == null) {
+                dumpNormal(fd, pw, args);
+                return;
+            }
+
+            String priority = null;
+            for (int argIndex = 0; argIndex < args.length; argIndex++) {
+                if (args[argIndex].equals(PRIORITY_ARG) && argIndex + 1 < args.length) {
+                    argIndex++;
+                    priority = args[argIndex];
+                }
+            }
+
+            if (PRIORITY_ARG_HIGH.equals(priority)) {
+                dumpHigh(fd, pw);
+            } else if (PRIORITY_ARG_NORMAL.equals(priority)) {
+                dumpNormal(fd, pw, args);
+            } else {
+                // ConnectivityService publishes binder service using publishBinderService() with
+                // no priority assigned will be treated as NORMAL priority. Dumpsys does not send
+                // "--dump-priority" arguments to the service. Thus, dump both NORMAL and HIGH to
+                // align the legacy design.
+                // TODO: Integrate into signal dump.
+                dumpNormal(fd, pw, args);
+                pw.println();
+                pw.println("DUMP OF SERVICE HIGH connectivity");
+                pw.println();
+                dumpHigh(fd, pw);
+            }
         }
-    };
+    }
 
     /**
      * Keeps track of the number of requests made under different uids.
@@ -983,6 +1111,13 @@
         }
 
         /**
+         * Get the {@link ConnectivityResources} to use in ConnectivityService.
+         */
+        public ConnectivityResources getResources(@NonNull Context ctx) {
+            return new ConnectivityResources(ctx);
+        }
+
+        /**
          * Create a HandlerThread to use in ConnectivityService.
          */
         public HandlerThread makeHandlerThread() {
@@ -1014,8 +1149,8 @@
         /**
          * @see NetworkUtils#queryUserAccess(int, int)
          */
-        public boolean queryUserAccess(int uid, int netId) {
-            return NetworkUtils.queryUserAccess(uid, netId);
+        public boolean queryUserAccess(int uid, Network network, ConnectivityService cs) {
+            return cs.queryUserAccess(uid, network);
         }
 
         /**
@@ -1035,19 +1170,19 @@
             return new MultinetworkPolicyTracker(c, h, r);
         }
 
-        public IBatteryStats getBatteryStatsService() {
-            return BatteryStatsService.getService();
-        }
-
         /**
          * @see BatteryStatsManager
          */
         public void reportNetworkInterfaceForTransports(Context context, String iface,
                 int[] transportTypes) {
-            final BatteryStatsManager  batteryStats =
+            final BatteryStatsManager batteryStats =
                     context.getSystemService(BatteryStatsManager.class);
             batteryStats.reportNetworkInterfaceForTransports(iface, transportTypes);
         }
+
+        public boolean getCellular464XlatEnabled() {
+            return NetworkProperties.isCellular464XlatEnabled().orElse(true);
+        }
     }
 
     public ConnectivityService(Context context) {
@@ -1064,13 +1199,15 @@
         mSystemProperties = mDeps.getSystemProperties();
         mNetIdManager = mDeps.makeNetIdManager();
         mContext = Objects.requireNonNull(context, "missing Context");
+        mResources = deps.getResources(mContext);
         mNetworkRequestCounter = new PerUidCounter(MAX_NETWORK_REQUESTS_PER_UID);
 
         mMetricsLog = logger;
         mNetworkRanker = new NetworkRanker();
         final NetworkRequest defaultInternetRequest = createDefaultRequest();
         mDefaultRequest = new NetworkRequestInfo(
-                defaultInternetRequest, null, new Binder(),
+                Process.myUid(), defaultInternetRequest, null,
+                new Binder(), NetworkCallback.FLAG_INCLUDE_LOCATION_INFO,
                 null /* attributionTags */);
         mNetworkRequests.put(defaultInternetRequest, mDefaultRequest);
         mDefaultNetworkRequests.add(mDefaultRequest);
@@ -1097,7 +1234,7 @@
                 new ConnectivityDiagnosticsHandler(mHandlerThread.getLooper());
 
         mReleasePendingIntentDelayMs = Settings.Secure.getInt(context.getContentResolver(),
-                Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
+                ConnectivitySettingsManager.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
 
         mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
         // TODO: Consider making the timer customizable.
@@ -1105,9 +1242,6 @@
 
         mStatsManager = mContext.getSystemService(NetworkStatsManager.class);
         mPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
-        mPolicyManagerInternal = Objects.requireNonNull(
-                LocalServices.getService(NetworkPolicyManagerInternal.class),
-                "missing NetworkPolicyManagerInternal");
         mDnsResolver = Objects.requireNonNull(dnsresolver, "missing IDnsResolver");
         mProxyTracker = mDeps.makeProxyTracker(mContext, mHandler);
 
@@ -1116,86 +1250,28 @@
         mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
         mLocationPermissionChecker = new LocationPermissionChecker(mContext);
 
-        // To ensure uid rules are synchronized with Network Policy, register for
+        // To ensure uid state is synchronized with Network Policy, register for
         // NetworkPolicyManagerService events must happen prior to NetworkPolicyManagerService
         // reading existing policy from disk.
-        mPolicyManager.registerListener(mPolicyListener);
+        mPolicyManager.registerNetworkPolicyCallback(null, mPolicyCallback);
 
         final PowerManager powerManager = (PowerManager) context.getSystemService(
                 Context.POWER_SERVICE);
         mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-        mNetTransitionWakeLockTimeout = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_networkTransitionTimeout);
         mPendingIntentWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
 
-        mNetConfigs = new NetworkConfig[ConnectivityManager.MAX_NETWORK_TYPE+1];
-
-        // TODO: What is the "correct" way to do determine if this is a wifi only device?
-        boolean wifiOnly = mSystemProperties.getBoolean("ro.radio.noril", false);
-        log("wifiOnly=" + wifiOnly);
-        String[] naStrings = context.getResources().getStringArray(
-                com.android.internal.R.array.networkAttributes);
-        for (String naString : naStrings) {
-            try {
-                NetworkConfig n = new NetworkConfig(naString);
-                if (VDBG) log("naString=" + naString + " config=" + n);
-                if (n.type > ConnectivityManager.MAX_NETWORK_TYPE) {
-                    loge("Error in networkAttributes - ignoring attempt to define type " +
-                            n.type);
-                    continue;
-                }
-                if (wifiOnly && ConnectivityManager.isNetworkTypeMobile(n.type)) {
-                    log("networkAttributes - ignoring mobile as this dev is wifiOnly " +
-                            n.type);
-                    continue;
-                }
-                if (mNetConfigs[n.type] != null) {
-                    loge("Error in networkAttributes - ignoring attempt to redefine type " +
-                            n.type);
-                    continue;
-                }
-                mLegacyTypeTracker.addSupportedType(n.type);
-
-                mNetConfigs[n.type] = n;
-                mNetworksDefined++;
-            } catch(Exception e) {
-                // ignore it - leave the entry null
-            }
-        }
-
-        // Forcibly add TYPE_VPN as a supported type, if it has not already been added via config.
-        if (mNetConfigs[TYPE_VPN] == null) {
-            // mNetConfigs is used only for "restore time", which isn't applicable to VPNs, so we
-            // don't need to add TYPE_VPN to mNetConfigs.
-            mLegacyTypeTracker.addSupportedType(TYPE_VPN);
-            mNetworksDefined++;  // used only in the log() statement below.
-        }
-
-        // Do the same for Ethernet, since it's often not specified in the configs, although many
-        // devices can use it via USB host adapters.
-        if (mNetConfigs[TYPE_ETHERNET] == null
-                && mContext.getSystemService(Context.ETHERNET_SERVICE) != null) {
-            mLegacyTypeTracker.addSupportedType(TYPE_ETHERNET);
-            mNetworksDefined++;
-        }
-
-        if (VDBG) log("mNetworksDefined=" + mNetworksDefined);
-
-        mProtectedNetworks = new ArrayList<Integer>();
+        mLegacyTypeTracker.loadSupportedTypes(mContext, mTelephonyManager);
+        mProtectedNetworks = new ArrayList<>();
         int[] protectedNetworks = context.getResources().getIntArray(
                 com.android.internal.R.array.config_protectedNetworks);
         for (int p : protectedNetworks) {
-            if ((mNetConfigs[p] != null) && (mProtectedNetworks.contains(p) == false)) {
+            if (mLegacyTypeTracker.isTypeSupported(p) && !mProtectedNetworks.contains(p)) {
                 mProtectedNetworks.add(p);
             } else {
                 if (DBG) loge("Ignoring protectedNetwork " + p);
             }
         }
 
-        mWolSupportedInterfaces = new ArraySet(
-                mContext.getResources().getStringArray(
-                        com.android.internal.R.array.config_wakeonlan_supported_interfaces));
-
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
 
         mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
@@ -1227,10 +1303,10 @@
         mQosCallbackTracker = new QosCallbackTracker(mHandler, mNetworkRequestCounter);
 
         final int dailyLimit = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT,
+                ConnectivitySettingsManager.NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT,
                 LingerMonitor.DEFAULT_NOTIFICATION_DAILY_LIMIT);
         final long rateLimit = Settings.Global.getLong(mContext.getContentResolver(),
-                Settings.Global.NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS,
+                ConnectivitySettingsManager.NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS,
                 LingerMonitor.DEFAULT_NOTIFICATION_RATE_LIMIT_MILLIS);
         mLingerMonitor = new LingerMonitor(mContext, mNotifier, dailyLimit, rateLimit);
 
@@ -1241,21 +1317,31 @@
         mDnsManager = new DnsManager(mContext, mDnsResolver);
         registerPrivateDnsSettingsCallbacks();
 
+        // This NAI is a sentinel used to offer no service to apps that are on a multi-layer
+        // request that doesn't allow fallback to the default network. It should never be visible
+        // to apps. As such, it's not in the list of NAIs and doesn't need many of the normal
+        // 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 NetworkInfo(TYPE_NONE, 0, "", ""),
-                new LinkProperties(), new NetworkCapabilities(), 0, mContext,
-                null, new NetworkAgentConfig(), this, null,
-                null, 0, INVALID_UID,
-                mQosCallbackTracker);
+                new LinkProperties(), new NetworkCapabilities(),
+                new NetworkScore.Builder().setLegacyInt(0).build(), mContext, null,
+                new NetworkAgentConfig(), this, null, null, 0, INVALID_UID, mQosCallbackTracker,
+                mDeps);
     }
 
     private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
+        return createDefaultNetworkCapabilitiesForUidRange(new UidRange(uid, uid));
+    }
+
+    private static NetworkCapabilities createDefaultNetworkCapabilitiesForUidRange(
+            @NonNull final UidRange uids) {
         final NetworkCapabilities netCap = new NetworkCapabilities();
         netCap.addCapability(NET_CAPABILITY_INTERNET);
         netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
-        netCap.setSingleUid(uid);
+        netCap.setUids(UidRange.toIntRanges(Collections.singleton(uids)));
         return netCap;
     }
 
@@ -1327,7 +1413,8 @@
 
         if (enable) {
             handleRegisterNetworkRequest(new NetworkRequestInfo(
-                    networkRequest, null, new Binder(),
+                    Process.myUid(), networkRequest, null, new Binder(),
+                    NetworkCallback.FLAG_INCLUDE_LOCATION_INFO,
                     null /* attributionTags */));
         } else {
             handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID,
@@ -1336,10 +1423,10 @@
     }
 
     private void handleConfigureAlwaysOnNetworks() {
-        handleAlwaysOnNetworkRequest(
-                mDefaultMobileDataRequest, Settings.Global.MOBILE_DATA_ALWAYS_ON, true);
-        handleAlwaysOnNetworkRequest(mDefaultWifiRequest, Settings.Global.WIFI_ALWAYS_REQUESTED,
-                false);
+        handleAlwaysOnNetworkRequest(mDefaultMobileDataRequest,
+                ConnectivitySettingsManager.MOBILE_DATA_ALWAYS_ON, true /* defaultValue */);
+        handleAlwaysOnNetworkRequest(mDefaultWifiRequest,
+                ConnectivitySettingsManager.WIFI_ALWAYS_REQUESTED, false /* defaultValue */);
         handleAlwaysOnNetworkRequest(mDefaultVehicleRequest,
                 com.android.internal.R.bool.config_vehicleInternalNetworkAlwaysRequested);
     }
@@ -1352,12 +1439,12 @@
 
         // Watch for whether or not to keep mobile data always on.
         mSettingsObserver.observe(
-                Settings.Global.getUriFor(Settings.Global.MOBILE_DATA_ALWAYS_ON),
+                Settings.Global.getUriFor(ConnectivitySettingsManager.MOBILE_DATA_ALWAYS_ON),
                 EVENT_CONFIGURE_ALWAYS_ON_NETWORKS);
 
         // Watch for whether or not to keep wifi always on.
         mSettingsObserver.observe(
-                Settings.Global.getUriFor(Settings.Global.WIFI_ALWAYS_REQUESTED),
+                Settings.Global.getUriFor(ConnectivitySettingsManager.WIFI_ALWAYS_REQUESTED),
                 EVENT_CONFIGURE_ALWAYS_ON_NETWORKS);
     }
 
@@ -1473,7 +1560,7 @@
         final int requestId = nri.getActiveRequest() != null
                 ? nri.getActiveRequest().requestId : nri.mRequests.get(0).requestId;
         mNetworkInfoBlockingLogs.log(String.format(
-                "%s %d(%d) on netId %d", action, nri.mUid, requestId, net.getNetId()));
+                "%s %d(%d) on netId %d", action, nri.mAsUid, requestId, net.getNetId()));
     }
 
     /**
@@ -1489,11 +1576,11 @@
         // but only exists if an app asks about them or requests them. Ensure the requesting app
         // gets the type it asks for.
         filtered.setType(type);
-        final DetailedState state = isNetworkWithCapabilitiesBlocked(nc, uid, ignoreBlocked)
-                ? DetailedState.BLOCKED
-                : filtered.getDetailedState();
-        filtered.setDetailedState(getLegacyLockdownState(state),
-                "" /* reason */, null /* extraInfo */);
+        if (isNetworkWithCapabilitiesBlocked(nc, uid, ignoreBlocked)) {
+            filtered.setDetailedState(DetailedState.BLOCKED, null /* reason */,
+                    null /* extraInfo */);
+        }
+        filterForLegacyLockdown(filtered);
         return filtered;
     }
 
@@ -1569,8 +1656,8 @@
         final DetailedState state = isNetworkWithCapabilitiesBlocked(nc, uid, false)
                 ? DetailedState.BLOCKED
                 : DetailedState.DISCONNECTED;
-        info.setDetailedState(getLegacyLockdownState(state),
-                "" /* reason */, null /* extraInfo */);
+        info.setDetailedState(state, null /* reason */, null /* extraInfo */);
+        filterForLegacyLockdown(info);
         return info;
     }
 
@@ -1688,13 +1775,14 @@
                 result.put(
                         nai.network,
                         createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                nc, mDeps.getCallingUid(), callingPackageName,
+                                nc, false /* includeLocationSensitiveInfo */,
+                                getCallingPid(), mDeps.getCallingUid(), callingPackageName,
                                 callingAttributionTag));
             }
         }
 
         // No need to check mLockdownEnabled. If it's true, getVpnUnderlyingNetworks returns null.
-        final Network[] networks = getVpnUnderlyingNetworks(Binder.getCallingUid());
+        final Network[] networks = getVpnUnderlyingNetworks(mDeps.getCallingUid());
         if (null != networks) {
             for (final Network network : networks) {
                 final NetworkCapabilities nc = getNetworkCapabilitiesInternal(network);
@@ -1702,7 +1790,9 @@
                     result.put(
                             network,
                             createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                    nc, mDeps.getCallingUid(), callingPackageName,
+                                    nc,
+                                    false /* includeLocationSensitiveInfo */,
+                                    getCallingPid(), mDeps.getCallingUid(), callingPackageName,
                                     callingAttributionTag));
                 }
             }
@@ -1784,7 +1874,8 @@
         enforceAccessPermission();
         return createWithLocationInfoSanitizedIfNecessaryWhenParceled(
                 getNetworkCapabilitiesInternal(network),
-                mDeps.getCallingUid(), callingPackageName, callingAttributionTag);
+                false /* includeLocationSensitiveInfo */,
+                getCallingPid(), mDeps.getCallingUid(), callingPackageName, callingAttributionTag);
     }
 
     @VisibleForTesting
@@ -1803,38 +1894,137 @@
         return newNc;
     }
 
-    private boolean hasLocationPermission(int callerUid, @NonNull String callerPkgName,
-            @Nullable String callingAttributionTag) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            return mLocationPermissionChecker.checkLocationPermission(
-                    callerPkgName, callingAttributionTag, callerUid, null /* message */);
-        } finally {
-            Binder.restoreCallingIdentity(token);
+    /**
+     * Wrapper used to cache the permission check results performed for the corresponding
+     * app. This avoid performing multiple permission checks for different fields in
+     * NetworkCapabilities.
+     * Note: This wrapper does not support any sort of invalidation and thus must not be
+     * persistent or long-lived. It may only be used for the time necessary to
+     * compute the redactions required by one particular NetworkCallback or
+     * synchronous call.
+     */
+    private class RedactionPermissionChecker {
+        private final int mCallingPid;
+        private final int mCallingUid;
+        @NonNull private final String mCallingPackageName;
+        @Nullable private final String mCallingAttributionTag;
+
+        private Boolean mHasLocationPermission = null;
+        private Boolean mHasLocalMacAddressPermission = null;
+        private Boolean mHasSettingsPermission = null;
+
+        RedactionPermissionChecker(int callingPid, int callingUid,
+                @NonNull String callingPackageName, @Nullable String callingAttributionTag) {
+            mCallingPid = callingPid;
+            mCallingUid = callingUid;
+            mCallingPackageName = callingPackageName;
+            mCallingAttributionTag = callingAttributionTag;
         }
+
+        private boolean hasLocationPermissionInternal() {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return mLocationPermissionChecker.checkLocationPermission(
+                        mCallingPackageName, mCallingAttributionTag, mCallingUid,
+                        null /* message */);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        /**
+         * Returns whether the app holds location permission or not (might return cached result
+         * if the permission was already checked before).
+         */
+        public boolean hasLocationPermission() {
+            if (mHasLocationPermission == null) {
+                // If there is no cached result, perform the check now.
+                mHasLocationPermission = hasLocationPermissionInternal();
+            }
+            return mHasLocationPermission;
+        }
+
+        /**
+         * Returns whether the app holds local mac address permission or not (might return cached
+         * result if the permission was already checked before).
+         */
+        public boolean hasLocalMacAddressPermission() {
+            if (mHasLocalMacAddressPermission == null) {
+                // If there is no cached result, perform the check now.
+                mHasLocalMacAddressPermission =
+                        checkLocalMacAddressPermission(mCallingPid, mCallingUid);
+            }
+            return mHasLocalMacAddressPermission;
+        }
+
+        /**
+         * Returns whether the app holds settings permission or not (might return cached
+         * result if the permission was already checked before).
+         */
+        public boolean hasSettingsPermission() {
+            if (mHasSettingsPermission == null) {
+                // If there is no cached result, perform the check now.
+                mHasSettingsPermission = checkSettingsPermission(mCallingPid, mCallingUid);
+            }
+            return mHasSettingsPermission;
+        }
+    }
+
+    private static boolean shouldRedact(@NetworkCapabilities.RedactionType long redactions,
+            @NetworkCapabilities.NetCapability long redaction) {
+        return (redactions & redaction) != 0;
+    }
+
+    /**
+     * Use the provided |applicableRedactions| to check the receiving app's
+     * permissions and clear/set the corresponding bit in the returned bitmask. The bitmask
+     * returned will be used to ensure the necessary redactions are performed by NetworkCapabilities
+     * before being sent to the corresponding app.
+     */
+    private @NetworkCapabilities.RedactionType long retrieveRequiredRedactions(
+            @NetworkCapabilities.RedactionType long applicableRedactions,
+            @NonNull RedactionPermissionChecker redactionPermissionChecker,
+            boolean includeLocationSensitiveInfo) {
+        long redactions = applicableRedactions;
+        if (shouldRedact(redactions, REDACT_FOR_ACCESS_FINE_LOCATION)) {
+            if (includeLocationSensitiveInfo
+                    && redactionPermissionChecker.hasLocationPermission()) {
+                redactions &= ~REDACT_FOR_ACCESS_FINE_LOCATION;
+            }
+        }
+        if (shouldRedact(redactions, REDACT_FOR_LOCAL_MAC_ADDRESS)) {
+            if (redactionPermissionChecker.hasLocalMacAddressPermission()) {
+                redactions &= ~REDACT_FOR_LOCAL_MAC_ADDRESS;
+            }
+        }
+        if (shouldRedact(redactions, REDACT_FOR_NETWORK_SETTINGS)) {
+            if (redactionPermissionChecker.hasSettingsPermission()) {
+                redactions &= ~REDACT_FOR_NETWORK_SETTINGS;
+            }
+        }
+        return redactions;
     }
 
     @VisibleForTesting
     @Nullable
     NetworkCapabilities createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-            @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName,
+            @Nullable NetworkCapabilities nc, boolean includeLocationSensitiveInfo,
+            int callingPid, int callingUid, @NonNull String callingPkgName,
             @Nullable String callingAttributionTag) {
         if (nc == null) {
             return null;
         }
-        Boolean hasLocationPermission = null;
-        final NetworkCapabilities newNc;
         // Avoid doing location permission check if the transport info has no location sensitive
         // data.
-        if (nc.getTransportInfo() != null && nc.getTransportInfo().hasLocationSensitiveFields()) {
-            hasLocationPermission =
-                    hasLocationPermission(callerUid, callerPkgName, callingAttributionTag);
-            newNc = new NetworkCapabilities(nc, hasLocationPermission);
-        } else {
-            newNc = new NetworkCapabilities(nc, false /* parcelLocationSensitiveFields */);
-        }
+        final RedactionPermissionChecker redactionPermissionChecker =
+                new RedactionPermissionChecker(callingPid, callingUid, callingPkgName,
+                        callingAttributionTag);
+        final long redactions = retrieveRequiredRedactions(
+                nc.getApplicableRedactions(), redactionPermissionChecker,
+                includeLocationSensitiveInfo);
+        final NetworkCapabilities newNc = new NetworkCapabilities(nc, redactions);
         // Reset owner uid if not destined for the owner app.
-        if (callerUid != nc.getOwnerUid()) {
+        if (callingUid != nc.getOwnerUid()) {
             newNc.setOwnerUid(INVALID_UID);
             return newNc;
         }
@@ -1843,13 +2033,17 @@
             // Owner UIDs already checked above. No need to re-check.
             return newNc;
         }
-        if (hasLocationPermission == null) {
-            // Location permission not checked yet, check now for masking owner UID.
-            hasLocationPermission =
-                    hasLocationPermission(callerUid, callerPkgName, callingAttributionTag);
+        // If the calling does not want location sensitive data & target SDK >= S, then mask info.
+        // Else include the owner UID iff the calling has location permission to provide backwards
+        // compatibility for older apps.
+        if (!includeLocationSensitiveInfo
+                && isTargetSdkAtleast(
+                        Build.VERSION_CODES.S, callingUid, callingPkgName)) {
+            newNc.setOwnerUid(INVALID_UID);
+            return newNc;
         }
         // Reset owner uid if the app has no location permission.
-        if (!hasLocationPermission) {
+        if (!redactionPermissionChecker.hasLocationPermission()) {
             newNc.setOwnerUid(INVALID_UID);
         }
         return newNc;
@@ -1881,6 +2075,8 @@
     private void restrictRequestUidsForCallerAndSetRequestorInfo(NetworkCapabilities nc,
             int callerUid, String callerPackageName) {
         if (!checkSettingsPermission()) {
+            // There is no need to track the effective UID of the request here. If the caller lacks
+            // the settings permission, the effective UID is the same as the calling ID.
             nc.setSingleUid(callerUid);
         }
         nc.setRequestorUidAndPackageName(callerUid, callerPackageName);
@@ -1896,6 +2092,18 @@
         }
     }
 
+    @Override
+    public @RestrictBackgroundStatus int getRestrictBackgroundStatusByCaller() {
+        enforceAccessPermission();
+        final int callerUid = Binder.getCallingUid();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return mPolicyManager.getRestrictBackgroundStatus(callerUid);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     // TODO: Consider delete this function or turn it into a no-op method.
     @Override
     public NetworkState[] getAllNetworkState() {
@@ -2131,53 +2339,17 @@
         }
     }
 
-    private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() {
+    private final NetworkPolicyCallback mPolicyCallback = new NetworkPolicyCallback() {
         @Override
-        public void onUidRulesChanged(int uid, int uidRules) {
-            mHandler.sendMessage(mHandler.obtainMessage(EVENT_UID_RULES_CHANGED, uid, uidRules));
-        }
-        @Override
-        public void onRestrictBackgroundChanged(boolean restrictBackground) {
-            // caller is NPMS, since we only register with them
-            if (LOGD_BLOCKED_NETWORKINFO) {
-                log("onRestrictBackgroundChanged(restrictBackground=" + restrictBackground + ")");
-            }
-            mHandler.sendMessage(mHandler.obtainMessage(
-                    EVENT_DATA_SAVER_CHANGED, restrictBackground ? 1 : 0, 0));
+        public void onUidBlockedReasonChanged(int uid, @BlockedReason int blockedReasons) {
+            mHandler.sendMessage(mHandler.obtainMessage(EVENT_UID_BLOCKED_REASON_CHANGED,
+                    uid, blockedReasons));
         }
     };
 
-    void handleUidRulesChanged(int uid, int newRules) {
-        // skip update when we've already applied rules
-        final int oldRules = mUidRules.get(uid, RULE_NONE);
-        if (oldRules == newRules) return;
-
-        maybeNotifyNetworkBlockedForNewUidRules(uid, newRules);
-
-        if (newRules == RULE_NONE) {
-            mUidRules.delete(uid);
-        } else {
-            mUidRules.put(uid, newRules);
-        }
-    }
-
-    void handleRestrictBackgroundChanged(boolean restrictBackground) {
-        if (mRestrictBackground == restrictBackground) return;
-
-        final List<UidRange> blockedRanges = mVpnBlockedUidRanges;
-        for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
-            final boolean curMetered = nai.networkCapabilities.isMetered();
-            maybeNotifyNetworkBlocked(nai, curMetered, curMetered, mRestrictBackground,
-                    restrictBackground, blockedRanges, blockedRanges);
-        }
-
-        mRestrictBackground = restrictBackground;
-    }
-
-    private boolean isUidBlockedByRules(int uid, int uidRules, boolean isNetworkMetered,
-            boolean isBackgroundRestricted) {
-        return mPolicyManager.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered,
-                isBackgroundRestricted);
+    private void handleUidBlockedReasonChanged(int uid, @BlockedReason int blockedReasons) {
+        maybeNotifyNetworkBlockedForNewState(uid, blockedReasons);
+        setUidBlockedReasons(uid, blockedReasons);
     }
 
     private boolean checkAnyPermissionOf(String... permissions) {
@@ -2364,9 +2536,12 @@
         mContext.enforceCallingOrSelfPermission(KeepaliveTracker.PERMISSION, "ConnectivityService");
     }
 
-    // Public because it's used by mLockdownTracker.
-    public void sendConnectedBroadcast(NetworkInfo info) {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
+    private boolean checkLocalMacAddressPermission(int pid, int uid) {
+        return PERMISSION_GRANTED == mContext.checkPermission(
+                Manifest.permission.LOCAL_MAC_ADDRESS, pid, uid);
+    }
+
+    private void sendConnectedBroadcast(NetworkInfo info) {
         sendGeneralBroadcast(info, CONNECTIVITY_ACTION);
     }
 
@@ -2550,13 +2725,6 @@
         } catch (RemoteException | ServiceSpecificException e) {
             loge("Can't set TCP buffer sizes:" + e);
         }
-
-        final Integer rwndValue = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.TCP_DEFAULT_INIT_RWND,
-                    mSystemProperties.getInt("net.tcp.default_init_rwnd", 0));
-        if (rwndValue != 0) {
-            mSystemProperties.setTcpInitRwnd(rwndValue);
-        }
     }
 
     @Override
@@ -2573,9 +2741,8 @@
         // if the system property isn't set, use the value for the apn type
         int ret = RESTORE_DEFAULT_NETWORK_DELAY;
 
-        if ((networkType <= ConnectivityManager.MAX_NETWORK_TYPE) &&
-                (mNetConfigs[networkType] != null)) {
-            ret = mNetConfigs[networkType].restoreTime;
+        if (mLegacyTypeTracker.isTypeSupported(networkType)) {
+            ret = mLegacyTypeTracker.getRestoreTimerForType(networkType);
         }
         return ret;
     }
@@ -2603,7 +2770,7 @@
     @Override
     protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
             @Nullable String[] args) {
-        PriorityDump.dump(mPriorityDumper, fd, writer, args);
+        mPriorityDumper.dump(fd, writer, args);
     }
 
     private boolean checkDumpPermission(Context context, String tag, PrintWriter pw) {
@@ -2618,10 +2785,9 @@
         }
     }
 
-    private void doDump(FileDescriptor fd, PrintWriter writer, String[] args, boolean asProto) {
+    private void doDump(FileDescriptor fd, PrintWriter writer, String[] args) {
         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
         if (!checkDumpPermission(mContext, TAG, pw)) return;
-        if (asProto) return;
 
         if (CollectionUtils.contains(args, DIAG_ARG)) {
             dumpNetworkDiagnostics(pw);
@@ -2662,19 +2828,16 @@
         pw.decreaseIndent();
         pw.println();
 
-        pw.print("Restrict background: ");
-        pw.println(mRestrictBackground);
-        pw.println();
-
         pw.println("Status for known UIDs:");
         pw.increaseIndent();
-        final int size = mUidRules.size();
+        final int size = mUidBlockedReasons.size();
         for (int i = 0; i < size; i++) {
             // Don't crash if the array is modified while dumping in bugreports.
             try {
-                final int uid = mUidRules.keyAt(i);
-                final int uidRules = mUidRules.get(uid, RULE_NONE);
-                pw.println("UID=" + uid + " rules=" + uidRulesToString(uidRules));
+                final int uid = mUidBlockedReasons.keyAt(i);
+                final int blockedReasons = mUidBlockedReasons.valueAt(i);
+                pw.println("UID=" + uid + " blockedReasons="
+                        + blockedReasonsToString(blockedReasons));
             } catch (ArrayIndexOutOfBoundsException e) {
                 pw.println("  ArrayIndexOutOfBoundsException");
             } catch (ConcurrentModificationException e) {
@@ -2809,7 +2972,7 @@
             if (0 == defaultRequest.mRequests.size()) {
                 pw.println("none, this should never occur.");
             } else {
-                pw.println(defaultRequest.mRequests.get(0).networkCapabilities.getUids());
+                pw.println(defaultRequest.mRequests.get(0).networkCapabilities.getUidRanges());
             }
             pw.decreaseIndent();
             pw.decreaseIndent();
@@ -2864,22 +3027,6 @@
             super(looper);
         }
 
-        private boolean maybeHandleAsyncChannelMessage(Message msg) {
-            switch (msg.what) {
-                default:
-                    return false;
-                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
-                    handleAsyncChannelHalfConnect(msg);
-                    break;
-                }
-                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
-                    handleAsyncChannelDisconnected(msg);
-                    break;
-                }
-            }
-            return true;
-        }
-
         private void maybeHandleNetworkAgentMessage(Message msg) {
             final Pair<NetworkAgentInfo, Object> arg = (Pair<NetworkAgentInfo, Object>) msg.obj;
             final NetworkAgentInfo nai = arg.first;
@@ -2920,12 +3067,15 @@
                     break;
                 }
                 case NetworkAgent.EVENT_NETWORK_SCORE_CHANGED: {
-                    updateNetworkScore(nai, msg.arg1);
+                    updateNetworkScore(nai, (NetworkScore) arg.second);
                     break;
                 }
                 case NetworkAgent.EVENT_SET_EXPLICITLY_SELECTED: {
                     if (nai.everConnected) {
                         loge("ERROR: cannot call explicitlySelected on already-connected network");
+                        // Note that if the NAI had been connected, this would affect the
+                        // score, and therefore would require re-mixing the score and performing
+                        // a rematch.
                     }
                     nai.networkAgentConfig.explicitlySelected = toBool(msg.arg1);
                     nai.networkAgentConfig.acceptUnvalidated = toBool(msg.arg1) && toBool(msg.arg2);
@@ -3031,7 +3181,8 @@
                         nai.lastCaptivePortalDetected = visible;
                         nai.everCaptivePortalDetected |= visible;
                         if (nai.lastCaptivePortalDetected &&
-                            Settings.Global.CAPTIVE_PORTAL_MODE_AVOID == getCaptivePortalMode()) {
+                                ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE_AVOID
+                                        == getCaptivePortalMode()) {
                             if (DBG) log("Avoiding captive portal network: " + nai.toShortString());
                             nai.onPreventAutomaticReconnect();
                             teardownUnneededNetwork(nai);
@@ -3142,8 +3293,8 @@
 
         private int getCaptivePortalMode() {
             return Settings.Global.getInt(mContext.getContentResolver(),
-                    Settings.Global.CAPTIVE_PORTAL_MODE,
-                    Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
+                    ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE,
+                    ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE_PROMPT);
         }
 
         private boolean maybeHandleNetworkAgentInfoMessage(Message msg) {
@@ -3171,8 +3322,7 @@
 
         @Override
         public void handleMessage(Message msg) {
-            if (!maybeHandleAsyncChannelMessage(msg)
-                    && !maybeHandleNetworkMonitorMessage(msg)
+            if (!maybeHandleNetworkMonitorMessage(msg)
                     && !maybeHandleNetworkAgentInfoMessage(msg)) {
                 maybeHandleNetworkAgentMessage(msg);
             }
@@ -3436,21 +3586,6 @@
         return false;
     }
 
-    private void handleAsyncChannelHalfConnect(Message msg) {
-        ensureRunningOnConnectivityServiceThread();
-        if (mNetworkProviderInfos.containsKey(msg.replyTo)) {
-            if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
-                if (VDBG) log("NetworkFactory connected");
-                // Finish setting up the full connection
-                NetworkProviderInfo npi = mNetworkProviderInfos.get(msg.replyTo);
-                sendAllRequestsToProvider(npi);
-            } else {
-                loge("Error connecting NetworkFactory");
-                mNetworkProviderInfos.remove(msg.obj);
-            }
-        }
-    }
-
     private void handleNetworkAgentRegistered(Message msg) {
         final NetworkAgentInfo nai = (NetworkAgentInfo) msg.obj;
         if (!mNetworkAgentInfos.contains(nai)) {
@@ -3481,14 +3616,6 @@
         }
     }
 
-    // This is a no-op if it's called with a message designating a provider that has
-    // already been destroyed, because its reference will not be found in the relevant
-    // maps.
-    private void handleAsyncChannelDisconnected(Message msg) {
-        NetworkProviderInfo npi = mNetworkProviderInfos.remove(msg.replyTo);
-        if (DBG && npi != null) log("unregisterNetworkFactory for " + npi.name);
-    }
-
     // Destroys a network, remove references to it from the internal state managed by
     // ConnectivityService, free its interfaces and clean up.
     // Must be called on the Handler thread.
@@ -3577,6 +3704,7 @@
             mDnsManager.removeNetwork(nai.network);
         }
         mNetIdManager.releaseNetId(nai.network.getNetId());
+        nai.onNetworkDisconnected();
     }
 
     private boolean createNativeNetwork(@NonNull NetworkAgentInfo networkAgent) {
@@ -3635,7 +3763,7 @@
                 log("Replacing " + existingRequest.mRequests.get(0) + " with "
                         + nri.mRequests.get(0) + " because their intents matched.");
             }
-            handleReleaseNetworkRequest(existingRequest.mRequests.get(0), getCallingUid(),
+            handleReleaseNetworkRequest(existingRequest.mRequests.get(0), mDeps.getCallingUid(),
                     /* callOnUnavailable */ false);
         }
         handleRegisterNetworkRequest(nri);
@@ -3651,6 +3779,7 @@
             mNetworkRequestInfoLogs.log("REGISTER " + nri);
             for (final NetworkRequest req : nri.mRequests) {
                 mNetworkRequests.put(req, nri);
+                // TODO: Consider update signal strength for other types.
                 if (req.isListen()) {
                     for (final NetworkAgentInfo network : mNetworkAgentInfos) {
                         if (req.networkCapabilities.hasSignalStrength()
@@ -3743,18 +3872,19 @@
         // listen requests won't keep up a network satisfying it. If this is not a multilayer
         // request, return immediately. For multilayer requests, check to see if any of the
         // multilayer requests may have a potential satisfier.
-        if (!nri.isMultilayerRequest() && nri.mRequests.get(0).isListen()) {
+        if (!nri.isMultilayerRequest() && (nri.mRequests.get(0).isListen()
+                || nri.mRequests.get(0).isListenForBest())) {
             return false;
         }
         for (final NetworkRequest req : nri.mRequests) {
             // This multilayer listen request is satisfied therefore no further requests need to be
             // evaluated deeming this network not a potential satisfier.
-            if (req.isListen() && nri.getActiveRequest() == req) {
+            if ((req.isListen() || req.isListenForBest()) && nri.getActiveRequest() == req) {
                 return false;
             }
             // As non-multilayer listen requests have already returned, the below would only happen
             // for a multilayer request therefore continue to the next request if available.
-            if (req.isListen()) {
+            if (req.isListen() || req.isListenForBest()) {
                 continue;
             }
             // If this Network is already the highest scoring Network for a request, or if
@@ -3794,8 +3924,7 @@
                 ? mNetworkRequests.get(request) : getNriForAppRequest(request);
 
         if (nri != null) {
-            if (Process.SYSTEM_UID != callingUid && Process.NETWORK_STACK_UID != callingUid
-                    && nri.mUid != callingUid) {
+            if (Process.SYSTEM_UID != callingUid && nri.mUid != callingUid) {
                 log(String.format("UID %d attempted to %s for unowned request %s",
                         callingUid, requestedOperation, nri));
                 return null;
@@ -4032,6 +4161,7 @@
             // network, we should respect the user's option and don't need to popup the
             // PARTIAL_CONNECTIVITY notification to user again.
             nai.networkAgentConfig.acceptPartialConnectivity = accept;
+            nai.updateScoreForNetworkAgentConfigUpdate();
             rematchAllNetworksAndRequests();
             sendUpdatedScoreToFactories(nai);
         }
@@ -4294,7 +4424,7 @@
 
         Intent intent = new Intent(action);
         if (type != NotificationType.PRIVATE_DNS_BROKEN) {
-            intent.setData(Uri.fromParts("netId", Integer.toString(nai.network.getNetId()), null));
+            intent.putExtra(ConnectivityManager.EXTRA_NETWORK, nai.network);
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             // Some OEMs have their own Settings package. Thus, need to get the current using
             // Settings package name instead of just use default name "com.android.settings".
@@ -4390,7 +4520,13 @@
         final NetworkPolicyManager netPolicyManager =
                  mContext.getSystemService(NetworkPolicyManager.class);
 
-        final int networkPreference = netPolicyManager.getMultipathPreference(network);
+        final long token = Binder.clearCallingIdentity();
+        final int networkPreference;
+        try {
+            networkPreference = netPolicyManager.getMultipathPreference(network);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
         if (networkPreference != 0) {
             return networkPreference;
         }
@@ -4420,7 +4556,8 @@
                     break;
                 }
                 case EVENT_PROXY_HAS_CHANGED: {
-                    handleApplyDefaultProxy((ProxyInfo)msg.obj);
+                    final Pair<Network, ProxyInfo> arg = (Pair<Network, ProxyInfo>) msg.obj;
+                    handleApplyDefaultProxy(arg.second);
                     break;
                 }
                 case EVENT_REGISTER_NETWORK_PROVIDER: {
@@ -4508,22 +4645,24 @@
                     handlePrivateDnsValidationUpdate(
                             (PrivateDnsValidationUpdate) msg.obj);
                     break;
-                case EVENT_UID_RULES_CHANGED:
-                    handleUidRulesChanged(msg.arg1, msg.arg2);
-                    break;
-                case EVENT_DATA_SAVER_CHANGED:
-                    handleRestrictBackgroundChanged(toBool(msg.arg1));
+                case EVENT_UID_BLOCKED_REASON_CHANGED:
+                    handleUidBlockedReasonChanged(msg.arg1, msg.arg2);
                     break;
                 case EVENT_SET_REQUIRE_VPN_FOR_UIDS:
                     handleSetRequireVpnForUids(toBool(msg.arg1), (UidRange[]) msg.obj);
                     break;
                 case EVENT_SET_OEM_NETWORK_PREFERENCE: {
-                    final Pair<OemNetworkPreferences, IOnSetOemNetworkPreferenceListener> arg =
-                            (Pair<OemNetworkPreferences,
-                                    IOnSetOemNetworkPreferenceListener>) msg.obj;
+                    final Pair<OemNetworkPreferences, IOnCompleteListener> arg =
+                            (Pair<OemNetworkPreferences, IOnCompleteListener>) msg.obj;
                     handleSetOemNetworkPreference(arg.first, arg.second);
                     break;
                 }
+                case EVENT_SET_PROFILE_NETWORK_PREFERENCE: {
+                    final Pair<ProfileNetworkPreferences.Preference, IOnCompleteListener> arg =
+                            (Pair<ProfileNetworkPreferences.Preference, IOnCompleteListener>)
+                                    msg.obj;
+                    handleSetProfileNetworkPreference(arg.first, arg.second);
+                }
                 case EVENT_REPORT_NETWORK_ACTIVITY:
                     mNetworkActivityTracker.handleReportNetworkActivity();
                     break;
@@ -4597,7 +4736,9 @@
         }
         mWakelockLogs.log("ACQUIRE for " + forWhom);
         Message msg = mHandler.obtainMessage(EVENT_EXPIRE_NET_TRANSITION_WAKELOCK);
-        mHandler.sendMessageDelayed(msg, mNetTransitionWakeLockTimeout);
+        final int lockTimeout = mResources.get().getInteger(
+                com.android.connectivity.resources.R.integer.config_networkTransitionTimeout);
+        mHandler.sendMessageDelayed(msg, lockTimeout);
     }
 
     // Called when we gain a new default network to release the network transition wakelock in a
@@ -4702,6 +4843,42 @@
         nai.networkMonitor().forceReevaluation(uid);
     }
 
+    // TODO: call into netd.
+    private boolean queryUserAccess(int uid, Network network) {
+        final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+        if (nai == null) return false;
+
+        // Any UID can use its default network.
+        if (nai == getDefaultNetworkForUid(uid)) return true;
+
+        // Privileged apps can use any network.
+        if (mPermissionMonitor.hasRestrictedNetworksPermission(uid)) {
+            return true;
+        }
+
+        // An unprivileged UID can use a VPN iff the VPN applies to it.
+        if (nai.isVPN()) {
+            return nai.networkCapabilities.appliesToUid(uid);
+        }
+
+        // An unprivileged UID can bypass the VPN that applies to it only if it can protect its
+        // sockets, i.e., if it is the owner.
+        final NetworkAgentInfo vpn = getVpnForUid(uid);
+        if (vpn != null && !vpn.networkAgentConfig.allowBypass
+                && uid != vpn.networkCapabilities.getOwnerUid()) {
+            return false;
+        }
+
+        // The UID's permission must be at least sufficient for the network. Since the restricted
+        // permission was already checked above, that just leaves background networks.
+        if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_FOREGROUND)) {
+            return mPermissionMonitor.hasUseBackgroundNetworksPermission(uid);
+        }
+
+        // Unrestricted network. Anyone gets to use it.
+        return true;
+    }
+
     /**
      * Returns information about the proxy a certain network is using. If given a null network, it
      * it will return the proxy for the bound network for the caller app or the default proxy if
@@ -4722,7 +4899,7 @@
                 return null;
             }
             return getLinkPropertiesProxyInfo(activeNetwork);
-        } else if (mDeps.queryUserAccess(mDeps.getCallingUid(), network.getNetId())) {
+        } else if (mDeps.queryUserAccess(mDeps.getCallingUid(), network, this)) {
             // Don't call getLinkProperties() as it requires ACCESS_NETWORK_STATE permission, which
             // caller may not have.
             return getLinkPropertiesProxyInfo(network);
@@ -4820,6 +4997,10 @@
         Log.wtf(TAG, s);
     }
 
+    private static void logwtf(String s, Throwable t) {
+        Log.wtf(TAG, s, t);
+    }
+
     private static void loge(String s) {
         Log.e(TAG, s);
     }
@@ -4945,7 +5126,7 @@
 
     @Override
     public void setRequireVpnForUids(boolean requireVpn, UidRange[] ranges) {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
+        enforceNetworkStackOrSettingsPermission();
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_REQUIRE_VPN_FOR_UIDS,
                 encodeBool(requireVpn), 0 /* arg2 */, ranges));
     }
@@ -4974,8 +5155,8 @@
 
         for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
             final boolean curMetered = nai.networkCapabilities.isMetered();
-            maybeNotifyNetworkBlocked(nai, curMetered, curMetered, mRestrictBackground,
-                    mRestrictBackground, mVpnBlockedUidRanges, newVpnBlockedUidRanges);
+            maybeNotifyNetworkBlocked(nai, curMetered, curMetered,
+                    mVpnBlockedUidRanges, newVpnBlockedUidRanges);
         }
 
         mVpnBlockedUidRanges = newVpnBlockedUidRanges;
@@ -4983,7 +5164,7 @@
 
     @Override
     public void setLegacyLockdownVpnEnabled(boolean enabled) {
-        enforceSettingsPermission();
+        enforceNetworkStackOrSettingsPermission();
         mHandler.post(() -> mLockdownEnabled = enabled);
     }
 
@@ -5012,8 +5193,8 @@
         // The legacy lockdown VPN always uses the default network.
         // If the VPN's underlying network is no longer the current default network, it means that
         // the default network has just switched, and the VPN is about to disconnect.
-        // Report that the VPN is not connected, so when the state of NetworkInfo objects
-        // overwritten by getLegacyLockdownState will be set to CONNECTING and not CONNECTED.
+        // Report that the VPN is not connected, so the state of NetworkInfo objects overwritten
+        // by filterForLegacyLockdown will be set to CONNECTING and not CONNECTED.
         final NetworkAgentInfo defaultNetwork = getDefaultNetwork();
         if (defaultNetwork == null || !defaultNetwork.network.equals(underlying[0])) {
             return null;
@@ -5022,6 +5203,9 @@
         return nai;
     };
 
+    // TODO: move all callers to filterForLegacyLockdown and delete this method.
+    // This likely requires making sendLegacyNetworkBroadcast take a NetworkInfo object instead of
+    // just a DetailedState object.
     private DetailedState getLegacyLockdownState(DetailedState origState) {
         if (origState != DetailedState.CONNECTED) {
             return origState;
@@ -5031,6 +5215,23 @@
                 : DetailedState.CONNECTED;
     }
 
+    private void filterForLegacyLockdown(NetworkInfo ni) {
+        if (!mLockdownEnabled || !ni.isConnected()) return;
+        // The legacy lockdown VPN replaces the state of every network in CONNECTED state with the
+        // state of its VPN. This is to ensure that when an underlying network connects, apps will
+        // not see a CONNECTIVITY_ACTION broadcast for a network in state CONNECTED until the VPN
+        // comes up, at which point there is a new CONNECTIVITY_ACTION broadcast for the underlying
+        // network, this time with a state of CONNECTED.
+        //
+        // Now that the legacy lockdown code lives in ConnectivityService, and no longer has access
+        // to the internal state of the Vpn object, always replace the state with CONNECTING. This
+        // is not too far off the truth, since an always-on VPN, when not connected, is always
+        // trying to reconnect.
+        if (getLegacyLockdownNai() == null) {
+            ni.setDetailedState(DetailedState.CONNECTING, "", null);
+        }
+    }
+
     @Override
     public void setProvisioningNotificationVisible(boolean visible, int networkType,
             String action) {
@@ -5072,6 +5273,9 @@
 
     private void onUserRemoved(UserHandle user) {
         mPermissionMonitor.onUserRemoved(user);
+        // If there was a network preference for this user, remove it.
+        handleSetProfileNetworkPreference(new ProfileNetworkPreferences.Preference(user, null),
+                null /* listener */);
         if (mOemNetworkPreferences.getNetworkPreferences().size() > 0) {
             handleSetOemNetworkPreference(mOemNetworkPreferences, null);
         }
@@ -5109,8 +5313,8 @@
         private final IBinder.DeathRecipient mDeathRecipient;
         public final int providerId;
 
-        NetworkProviderInfo(String name, Messenger messenger, AsyncChannel asyncChannel,
-                int providerId, @NonNull IBinder.DeathRecipient deathRecipient) {
+        NetworkProviderInfo(String name, Messenger messenger, int providerId,
+                @NonNull IBinder.DeathRecipient deathRecipient) {
             this.name = name;
             this.messenger = messenger;
             this.providerId = providerId;
@@ -5200,12 +5404,22 @@
         boolean mPendingIntentSent;
         @Nullable
         final Messenger mMessenger;
+
+        // Information about the caller that caused this object to be created.
         @Nullable
         private final IBinder mBinder;
         final int mPid;
         final int mUid;
+        final @NetworkCallback.Flag int mCallbackFlags;
         @Nullable
         final String mCallingAttributionTag;
+
+        // Effective UID of this request. This is different from mUid when a privileged process
+        // files a request on behalf of another UID. This UID is used to determine blocked status,
+        // UID matching, and so on. mUid above is used for permission checks and to enforce the
+        // maximum limit of registered callbacks per UID.
+        final int mAsUid;
+
         // In order to preserve the mapping of NetworkRequest-to-callback when apps register
         // callbacks using a returned NetworkRequest, the original NetworkRequest needs to be
         // maintained for keying off of. This is only a concern when the original nri
@@ -5229,17 +5443,16 @@
         private Set<UidRange> getUids() {
             // networkCapabilities.getUids() returns a defensive copy.
             // multilayer requests will all have the same uids so return the first one.
-            final Set<UidRange> uids = null == mRequests.get(0).networkCapabilities.getUids()
-                    ? new ArraySet<>() : mRequests.get(0).networkCapabilities.getUids();
-            return uids;
+            final Set<UidRange> uids = mRequests.get(0).networkCapabilities.getUidRanges();
+            return (null == uids) ? new ArraySet<>() : uids;
         }
 
-        NetworkRequestInfo(@NonNull final NetworkRequest r, @Nullable final PendingIntent pi,
-                @Nullable String callingAttributionTag) {
-            this(Collections.singletonList(r), r, pi, callingAttributionTag);
+        NetworkRequestInfo(int asUid, @NonNull final NetworkRequest r,
+                @Nullable final PendingIntent pi, @Nullable String callingAttributionTag) {
+            this(asUid, Collections.singletonList(r), r, pi, callingAttributionTag);
         }
 
-        NetworkRequestInfo(@NonNull final List<NetworkRequest> r,
+        NetworkRequestInfo(int asUid, @NonNull final List<NetworkRequest> r,
                 @NonNull final NetworkRequest requestForCallback, @Nullable final PendingIntent pi,
                 @Nullable String callingAttributionTag) {
             ensureAllNetworkRequestsHaveType(r);
@@ -5250,18 +5463,29 @@
             mBinder = null;
             mPid = getCallingPid();
             mUid = mDeps.getCallingUid();
+            mAsUid = asUid;
             mNetworkRequestCounter.incrementCountOrThrow(mUid);
+            /**
+             * Location sensitive data not included in pending intent. Only included in
+             * {@link NetworkCallback}.
+             */
+            mCallbackFlags = NetworkCallback.FLAG_NONE;
             mCallingAttributionTag = callingAttributionTag;
         }
 
-        NetworkRequestInfo(@NonNull final NetworkRequest r, @Nullable final Messenger m,
-                @Nullable final IBinder binder, @Nullable String callingAttributionTag) {
-            this(Collections.singletonList(r), r, m, binder, callingAttributionTag);
+        NetworkRequestInfo(int asUid, @NonNull final NetworkRequest r, @Nullable final Messenger m,
+                @Nullable final IBinder binder,
+                @NetworkCallback.Flag int callbackFlags,
+                @Nullable String callingAttributionTag) {
+            this(asUid, Collections.singletonList(r), r, m, binder, callbackFlags,
+                    callingAttributionTag);
         }
 
-        NetworkRequestInfo(@NonNull final List<NetworkRequest> r,
+        NetworkRequestInfo(int asUid, @NonNull final List<NetworkRequest> r,
                 @NonNull final NetworkRequest requestForCallback, @Nullable final Messenger m,
-                @Nullable final IBinder binder, @Nullable String callingAttributionTag) {
+                @Nullable final IBinder binder,
+                @NetworkCallback.Flag int callbackFlags,
+                @Nullable String callingAttributionTag) {
             super();
             ensureAllNetworkRequestsHaveType(r);
             mRequests = initializeRequests(r);
@@ -5270,8 +5494,10 @@
             mBinder = binder;
             mPid = getCallingPid();
             mUid = mDeps.getCallingUid();
+            mAsUid = asUid;
             mPendingIntent = null;
             mNetworkRequestCounter.incrementCountOrThrow(mUid);
+            mCallbackFlags = callbackFlags;
             mCallingAttributionTag = callingAttributionTag;
 
             try {
@@ -5311,17 +5537,19 @@
             mBinder = nri.mBinder;
             mPid = nri.mPid;
             mUid = nri.mUid;
+            mAsUid = nri.mAsUid;
             mPendingIntent = nri.mPendingIntent;
             mNetworkRequestCounter.incrementCountOrThrow(mUid);
+            mCallbackFlags = nri.mCallbackFlags;
             mCallingAttributionTag = nri.mCallingAttributionTag;
         }
 
-        NetworkRequestInfo(@NonNull final NetworkRequest r) {
-            this(Collections.singletonList(r));
+        NetworkRequestInfo(int asUid, @NonNull final NetworkRequest r) {
+            this(asUid, Collections.singletonList(r));
         }
 
-        NetworkRequestInfo(@NonNull final List<NetworkRequest> r) {
-            this(r, r.get(0), null /* pi */, null /* callingAttributionTag */);
+        NetworkRequestInfo(int asUid, @NonNull final List<NetworkRequest> r) {
+            this(asUid, r, r.get(0), null /* pi */, null /* callingAttributionTag */);
         }
 
         // True if this NRI is being satisfied. It also accounts for if the nri has its satisifer
@@ -5357,12 +5585,14 @@
 
         @Override
         public String toString() {
-            return "uid/pid:" + mUid + "/" + mPid + " active request Id: "
+            final String asUidString = (mAsUid == mUid) ? "" : " asUid: " + mAsUid;
+            return "uid/pid:" + mUid + "/" + mPid + asUidString + " activeRequest: "
                     + (mActiveRequest == null ? null : mActiveRequest.requestId)
-                    + " callback request Id: "
+                    + " callbackRequest: "
                     + mNetworkRequestForCallback.requestId
                     + " " + mRequests
-                    + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
+                    + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent)
+                    + "callback flags: " + mCallbackFlags;
         }
     }
 
@@ -5446,30 +5676,37 @@
         }
     }
 
-    private boolean checkUnsupportedStartingFrom(int version, String callingPackageName) {
-        final UserHandle user = UserHandle.getUserHandleForUid(mDeps.getCallingUid());
+    private boolean isTargetSdkAtleast(int version, int callingUid,
+            @NonNull String callingPackageName) {
+        final UserHandle user = UserHandle.getUserHandleForUid(callingUid);
         final PackageManager pm =
                 mContext.createContextAsUser(user, 0 /* flags */).getPackageManager();
         try {
-            final int callingVersion = pm.getApplicationInfo(
-                    callingPackageName, 0 /* flags */).targetSdkVersion;
+            final int callingVersion = pm.getTargetSdkVersion(callingPackageName);
             if (callingVersion < version) return false;
         } catch (PackageManager.NameNotFoundException e) { }
         return true;
     }
 
     @Override
-    public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
+    public NetworkRequest requestNetwork(int asUid, NetworkCapabilities networkCapabilities,
             int reqTypeInt, Messenger messenger, int timeoutMs, IBinder binder,
-            int legacyType, @NonNull String callingPackageName,
+            int legacyType, int callbackFlags, @NonNull String callingPackageName,
             @Nullable String callingAttributionTag) {
         if (legacyType != TYPE_NONE && !checkNetworkStackPermission()) {
-            if (checkUnsupportedStartingFrom(Build.VERSION_CODES.M, callingPackageName)) {
+            if (isTargetSdkAtleast(Build.VERSION_CODES.M, mDeps.getCallingUid(),
+                    callingPackageName)) {
                 throw new SecurityException("Insufficient permissions to specify legacy type");
             }
         }
         final NetworkCapabilities defaultNc = mDefaultRequest.mRequests.get(0).networkCapabilities;
         final int callingUid = mDeps.getCallingUid();
+        // Privileged callers can track the default network of another UID by passing in a UID.
+        if (asUid != Process.INVALID_UID) {
+            enforceSettingsPermission();
+        } else {
+            asUid = callingUid;
+        }
         final NetworkRequest.Type reqType;
         try {
             reqType = NetworkRequest.Type.values()[reqTypeInt];
@@ -5479,10 +5716,10 @@
         switch (reqType) {
             case TRACK_DEFAULT:
                 // If the request type is TRACK_DEFAULT, the passed {@code networkCapabilities}
-                // is unused and will be replaced by ones appropriate for the caller.
-                // This allows callers to keep track of the default network for their app.
+                // is unused and will be replaced by ones appropriate for the UID (usually, the
+                // calling app). This allows callers to keep track of the default network.
                 networkCapabilities = copyDefaultNetworkCapabilitiesForUid(
-                        defaultNc, callingUid, callingPackageName);
+                        defaultNc, asUid, callingUid, callingPackageName);
                 enforceAccessPermission();
                 break;
             case TRACK_SYSTEM_DEFAULT:
@@ -5501,6 +5738,10 @@
                 //  request if the app changes network state. http://b/29964605
                 enforceMeteredApnPolicy(networkCapabilities);
                 break;
+            case LISTEN_FOR_BEST:
+                enforceAccessPermission();
+                networkCapabilities = new NetworkCapabilities(networkCapabilities);
+                break;
             default:
                 throw new IllegalArgumentException("Unsupported request type " + reqType);
         }
@@ -5508,11 +5749,17 @@
         ensureSufficientPermissionsForRequest(networkCapabilities,
                 Binder.getCallingPid(), callingUid, callingPackageName);
 
-        // Set the UID range for this request to the single UID of the requester, or to an empty
-        // set of UIDs if the caller has the appropriate permission and UIDs have not been set.
+        // Enforce FOREGROUND if the caller does not have permission to use background network.
+        if (reqType == LISTEN_FOR_BEST) {
+            restrictBackgroundRequestForCaller(networkCapabilities);
+        }
+
+        // Set the UID range for this request to the single UID of the requester, unless the
+        // requester has the permission to specify other UIDs.
         // This will overwrite any allowed UIDs in the requested capabilities. Though there
         // are no visible methods to set the UIDs, an app could use reflection to try and get
         // networks for other apps so it's essential that the UIDs are overwritten.
+        // Also set the requester UID and package name in the request.
         restrictRequestUidsForCallerAndSetRequestorInfo(networkCapabilities,
                 callingUid, callingPackageName);
 
@@ -5524,7 +5771,8 @@
         final NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
                 nextNetworkRequestId(), reqType);
         final NetworkRequestInfo nri = getNriToRegister(
-                networkRequest, messenger, binder, callingAttributionTag);
+                asUid, networkRequest, messenger, binder, callbackFlags,
+                callingAttributionTag);
         if (DBG) log("requestNetwork for " + nri);
 
         // For TRACK_SYSTEM_DEFAULT callbacks, the capabilities have been modified since they were
@@ -5551,23 +5799,27 @@
      * requests registered to track the default request. If there is currently a per-app default
      * tracking the app requestor, then we need to create a version of this nri that mirrors that of
      * the tracking per-app default so that callbacks are sent to the app requestor appropriately.
+     * @param asUid the uid on behalf of which to file the request. Different from requestorUid
+     *              when a privileged caller is tracking the default network for another uid.
      * @param nr the network request for the nri.
      * @param msgr the messenger for the nri.
      * @param binder the binder for the nri.
      * @param callingAttributionTag the calling attribution tag for the nri.
      * @return the nri to register.
      */
-    private NetworkRequestInfo getNriToRegister(@NonNull final NetworkRequest nr,
+    private NetworkRequestInfo getNriToRegister(final int asUid, @NonNull final NetworkRequest nr,
             @Nullable final Messenger msgr, @Nullable final IBinder binder,
+            @NetworkCallback.Flag int callbackFlags,
             @Nullable String callingAttributionTag) {
         final List<NetworkRequest> requests;
         if (NetworkRequest.Type.TRACK_DEFAULT == nr.type) {
             requests = copyDefaultNetworkRequestsForUid(
-                    nr.getRequestorUid(), nr.getRequestorPackageName());
+                    asUid, nr.getRequestorUid(), nr.getRequestorPackageName());
         } else {
             requests = Collections.singletonList(nr);
         }
-        return new NetworkRequestInfo(requests, nr, msgr, binder, callingAttributionTag);
+        return new NetworkRequestInfo(
+                asUid, requests, nr, msgr, binder, callbackFlags, callingAttributionTag);
     }
 
     private void enforceNetworkRequestPermissions(NetworkCapabilities networkCapabilities,
@@ -5648,8 +5900,8 @@
 
         NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
                 nextNetworkRequestId(), NetworkRequest.Type.REQUEST);
-        NetworkRequestInfo nri =
-                new NetworkRequestInfo(networkRequest, operation, callingAttributionTag);
+        NetworkRequestInfo nri = new NetworkRequestInfo(callingUid, networkRequest, operation,
+                callingAttributionTag);
         if (DBG) log("pendingRequest for " + nri);
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT,
                 nri));
@@ -5659,14 +5911,14 @@
     private void releasePendingNetworkRequestWithDelay(PendingIntent operation) {
         mHandler.sendMessageDelayed(
                 mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT,
-                getCallingUid(), 0, operation), mReleasePendingIntentDelayMs);
+                mDeps.getCallingUid(), 0, operation), mReleasePendingIntentDelayMs);
     }
 
     @Override
     public void releasePendingNetworkRequest(PendingIntent operation) {
         Objects.requireNonNull(operation, "PendingIntent cannot be null.");
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT,
-                getCallingUid(), 0, operation));
+                mDeps.getCallingUid(), 0, operation));
     }
 
     // In order to implement the compatibility measure for pre-M apps that call
@@ -5693,8 +5945,9 @@
 
     @Override
     public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
-            Messenger messenger, IBinder binder, @NonNull String callingPackageName,
-            @Nullable String callingAttributionTag) {
+            Messenger messenger, IBinder binder,
+            @NetworkCallback.Flag int callbackFlags,
+            @NonNull String callingPackageName, @NonNull String callingAttributionTag) {
         final int callingUid = mDeps.getCallingUid();
         if (!hasWifiNetworkListenPermission(networkCapabilities)) {
             enforceAccessPermission();
@@ -5715,7 +5968,8 @@
         NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
                 NetworkRequest.Type.LISTEN);
         NetworkRequestInfo nri =
-                new NetworkRequestInfo(networkRequest, messenger, binder, callingAttributionTag);
+                new NetworkRequestInfo(callingUid, networkRequest, messenger, binder, callbackFlags,
+                        callingAttributionTag);
         if (VDBG) log("listenForNetwork for " + nri);
 
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
@@ -5739,8 +5993,8 @@
 
         NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
                 NetworkRequest.Type.LISTEN);
-        NetworkRequestInfo nri =
-                new NetworkRequestInfo(networkRequest, operation, callingAttributionTag);
+        NetworkRequestInfo nri = new NetworkRequestInfo(callingUid, networkRequest, operation,
+                callingAttributionTag);
         if (VDBG) log("pendingListenForNetwork for " + nri);
 
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
@@ -5761,7 +6015,7 @@
     public void releaseNetworkRequest(NetworkRequest networkRequest) {
         ensureNetworkRequestHasType(networkRequest);
         mHandler.sendMessage(mHandler.obtainMessage(
-                EVENT_RELEASE_NETWORK_REQUEST, getCallingUid(), 0, networkRequest));
+                EVENT_RELEASE_NETWORK_REQUEST, mDeps.getCallingUid(), 0, networkRequest));
     }
 
     private void handleRegisterNetworkProvider(NetworkProviderInfo npi) {
@@ -5784,8 +6038,7 @@
     public int registerNetworkProvider(Messenger messenger, String name) {
         enforceNetworkFactoryOrSettingsPermission();
         NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger,
-                null /* asyncChannel */, nextNetworkProviderId(),
-                () -> unregisterNetworkProvider(messenger));
+                nextNetworkProviderId(), () -> unregisterNetworkProvider(messenger));
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi));
         return npi.providerId;
     }
@@ -5843,10 +6096,16 @@
     @GuardedBy("mBlockedAppUids")
     private final HashSet<Integer> mBlockedAppUids = new HashSet<>();
 
-    // Current OEM network preferences.
+    // Current OEM network preferences. This object must only be written to on the handler thread.
+    // Since it is immutable and always non-null, other threads may read it if they only care
+    // about seeing a consistent object but not that it is current.
     @NonNull
     private OemNetworkPreferences mOemNetworkPreferences =
             new OemNetworkPreferences.Builder().build();
+    // Current per-profile network preferences. This object follows the same threading rules as
+    // the OEM network preferences above.
+    @NonNull
+    private ProfileNetworkPreferences mProfileNetworkPreferences = new ProfileNetworkPreferences();
 
     // The always-on request for an Internet-capable network that apps without a specific default
     // fall back to.
@@ -5885,33 +6144,37 @@
     /**
      * Get a copy of the network requests of the default request that is currently tracking the
      * given uid.
+     * @param asUid the uid on behalf of which to file the request. Different from requestorUid
+     *              when a privileged caller is tracking the default network for another uid.
      * @param requestorUid the uid to check the default for.
      * @param requestorPackageName the requestor's package name.
      * @return a copy of the default's NetworkRequest that is tracking the given uid.
      */
     @NonNull
     private List<NetworkRequest> copyDefaultNetworkRequestsForUid(
-            @NonNull final int requestorUid, @NonNull final String requestorPackageName) {
+            final int asUid, final int requestorUid, @NonNull final String requestorPackageName) {
         return copyNetworkRequestsForUid(
-                getDefaultRequestTrackingUid(requestorUid).mRequests,
-                requestorUid, requestorPackageName);
+                getDefaultRequestTrackingUid(asUid).mRequests,
+                asUid, requestorUid, requestorPackageName);
     }
 
     /**
      * Copy the given nri's NetworkRequest collection.
      * @param requestsToCopy the NetworkRequest collection to be copied.
+     * @param asUid the uid on behalf of which to file the request. Different from requestorUid
+     *              when a privileged caller is tracking the default network for another uid.
      * @param requestorUid the uid to set on the copied collection.
      * @param requestorPackageName the package name to set on the copied collection.
      * @return the copied NetworkRequest collection.
      */
     @NonNull
     private List<NetworkRequest> copyNetworkRequestsForUid(
-            @NonNull final List<NetworkRequest> requestsToCopy, @NonNull final int requestorUid,
-            @NonNull final String requestorPackageName) {
+            @NonNull final List<NetworkRequest> requestsToCopy, final int asUid,
+            final int requestorUid, @NonNull final String requestorPackageName) {
         final List<NetworkRequest> requests = new ArrayList<>();
         for (final NetworkRequest nr : requestsToCopy) {
             requests.add(new NetworkRequest(copyDefaultNetworkCapabilitiesForUid(
-                            nr.networkCapabilities, requestorUid, requestorPackageName),
+                            nr.networkCapabilities, asUid, requestorUid, requestorPackageName),
                     nr.legacyType, nextNetworkRequestId(), nr.type));
         }
         return requests;
@@ -5919,12 +6182,17 @@
 
     @NonNull
     private NetworkCapabilities copyDefaultNetworkCapabilitiesForUid(
-            @NonNull final NetworkCapabilities netCapToCopy, @NonNull final int requestorUid,
-            @NonNull final String requestorPackageName) {
+            @NonNull final NetworkCapabilities netCapToCopy, final int asUid,
+            final int requestorUid, @NonNull final String requestorPackageName) {
+        // These capabilities are for a TRACK_DEFAULT callback, so:
+        // 1. Remove NET_CAPABILITY_VPN, because it's (currently!) the only difference between
+        //    mDefaultRequest and a per-UID default request.
+        //    TODO: stop depending on the fact that these two unrelated things happen to be the same
+        // 2. Always set the UIDs to asUid. restrictRequestUidsForCallerAndSetRequestorInfo will
+        //    not do this in the case of a privileged application.
         final NetworkCapabilities netCap = new NetworkCapabilities(netCapToCopy);
         netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
-        netCap.setSingleUid(requestorUid);
-        netCap.setUids(new ArraySet<>());
+        netCap.setSingleUid(asUid);
         restrictRequestUidsForCallerAndSetRequestorInfo(
                 netCap, requestorUid, requestorPackageName);
         return netCap;
@@ -6005,7 +6273,7 @@
         for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
             // Currently, all network requests will have the same uids therefore checking the first
             // one is sufficient. If/when uids are tracked at the nri level, this can change.
-            final Set<UidRange> uids = nri.mRequests.get(0).networkCapabilities.getUids();
+            final Set<UidRange> uids = nri.mRequests.get(0).networkCapabilities.getUidRanges();
             if (null == uids) {
                 continue;
             }
@@ -6036,20 +6304,6 @@
         return nai == getDefaultNetwork();
     }
 
-    // TODO : remove this method. It's a stopgap measure to help sheperding a number of dependent
-    // changes that would conflict throughout the automerger graph. Having this method temporarily
-    // helps with the process of going through with all these dependent changes across the entire
-    // tree.
-    /**
-     * Register a new agent. {@see #registerNetworkAgent} below.
-     */
-    public Network registerNetworkAgent(INetworkAgent na, NetworkInfo networkInfo,
-            LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
-            int currentScore, NetworkAgentConfig networkAgentConfig) {
-        return registerNetworkAgent(na, networkInfo, linkProperties, networkCapabilities,
-                currentScore, networkAgentConfig, NetworkProvider.ID_NONE);
-    }
-
     /**
      * Register a new agent with ConnectivityService to handle a network.
      *
@@ -6060,7 +6314,7 @@
      *         later : see {@link #updateLinkProperties}.
      * @param networkCapabilities the initial capabilites of this network. They can be updated
      *         later : see {@link #updateCapabilities}.
-     * @param currentScore the initial score of the network. See
+     * @param initialScore the initial score of the network. See
      *         {@link NetworkAgentInfo#getCurrentScore}.
      * @param networkAgentConfig metadata about the network. This is never updated.
      * @param providerId the ID of the provider owning this NetworkAgent.
@@ -6068,10 +6322,12 @@
      */
     public Network registerNetworkAgent(INetworkAgent na, NetworkInfo networkInfo,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
-            int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) {
+            @NonNull NetworkScore initialScore, NetworkAgentConfig networkAgentConfig,
+            int providerId) {
         Objects.requireNonNull(networkInfo, "networkInfo must not be null");
         Objects.requireNonNull(linkProperties, "linkProperties must not be null");
         Objects.requireNonNull(networkCapabilities, "networkCapabilities must not be null");
+        Objects.requireNonNull(initialScore, "initialScore must not be null");
         Objects.requireNonNull(networkAgentConfig, "networkAgentConfig must not be null");
         if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
             enforceAnyPermissionOf(Manifest.permission.MANAGE_TEST_NETWORKS);
@@ -6083,7 +6339,7 @@
         final long token = Binder.clearCallingIdentity();
         try {
             return registerNetworkAgentInternal(na, networkInfo, linkProperties,
-                    networkCapabilities, currentScore, networkAgentConfig, providerId, uid);
+                    networkCapabilities, initialScore, networkAgentConfig, providerId, uid);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -6091,7 +6347,8 @@
 
     private Network registerNetworkAgentInternal(INetworkAgent na, NetworkInfo networkInfo,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
-            int currentScore, NetworkAgentConfig networkAgentConfig, int providerId, int uid) {
+            NetworkScore currentScore, NetworkAgentConfig networkAgentConfig, int providerId,
+            int uid) {
         if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
             // Strictly, sanitizing here is unnecessary as the capabilities will be sanitized in
             // the call to mixInCapabilities below anyway, but sanitizing here means the NAI never
@@ -6106,7 +6363,7 @@
         final NetworkAgentInfo nai = new NetworkAgentInfo(na,
                 new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
                 currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig),
-                this, mNetd, mDnsResolver, providerId, uid, mQosCallbackTracker);
+                this, mNetd, mDnsResolver, providerId, uid, mQosCallbackTracker, mDeps);
 
         // Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says.
         processCapabilitiesFromAgent(nai, nc);
@@ -6457,7 +6714,7 @@
             return;
         }
 
-        final Set<UidRange> ranges = nai.networkCapabilities.getUids();
+        final Set<UidRange> ranges = nai.networkCapabilities.getUidRanges();
         final int vpnAppUid = nai.networkCapabilities.getOwnerUid();
         // TODO: this create a window of opportunity for apps to receive traffic between the time
         // when the old rules are removed and the time when new rules are added. To fix this,
@@ -6472,6 +6729,11 @@
     }
 
     private void updateWakeOnLan(@NonNull LinkProperties lp) {
+        if (mWolSupportedInterfaces == null) {
+            mWolSupportedInterfaces = new ArraySet<>(mResources.get().getStringArray(
+                    com.android.connectivity.resources.R.array
+                            .config_wakeonlan_supported_interfaces));
+        }
         lp.setWakeOnLanSupported(mWolSupportedInterfaces.contains(lp.getInterfaceName()));
     }
 
@@ -6708,8 +6970,8 @@
         final boolean meteredChanged = oldMetered != newMetered;
 
         if (meteredChanged) {
-            maybeNotifyNetworkBlocked(nai, oldMetered, newMetered, mRestrictBackground,
-                    mRestrictBackground, mVpnBlockedUidRanges, mVpnBlockedUidRanges);
+            maybeNotifyNetworkBlocked(nai, oldMetered, newMetered,
+                    mVpnBlockedUidRanges, mVpnBlockedUidRanges);
         }
 
         final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING)
@@ -6817,8 +7079,8 @@
 
     private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc,
             NetworkCapabilities newNc) {
-        Set<UidRange> prevRanges = null == prevNc ? null : prevNc.getUids();
-        Set<UidRange> newRanges = null == newNc ? null : newNc.getUids();
+        Set<UidRange> prevRanges = null == prevNc ? null : prevNc.getUidRanges();
+        Set<UidRange> newRanges = null == newNc ? null : newNc.getUidRanges();
         if (null == prevRanges) prevRanges = new ArraySet<>();
         if (null == newRanges) newRanges = new ArraySet<>();
         final Set<UidRange> prevRangesCopy = new ArraySet<>(prevRanges);
@@ -7044,6 +7306,8 @@
         if (notificationType != ConnectivityManager.CALLBACK_UNAVAIL) {
             putParcelable(bundle, networkAgent.network);
         }
+        final boolean includeLocationSensitiveInfo =
+                (nri.mCallbackFlags & NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) != 0;
         switch (notificationType) {
             case ConnectivityManager.CALLBACK_AVAILABLE: {
                 final NetworkCapabilities nc =
@@ -7052,7 +7316,8 @@
                 putParcelable(
                         bundle,
                         createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                nc, nri.mUid, nrForCallback.getRequestorPackageName(),
+                                nc, includeLocationSensitiveInfo, nri.mPid, nri.mUid,
+                                nrForCallback.getRequestorPackageName(),
                                 nri.mCallingAttributionTag));
                 putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
                         networkAgent.linkProperties, nri.mPid, nri.mUid));
@@ -7072,7 +7337,8 @@
                 putParcelable(
                         bundle,
                         createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                netCap, nri.mUid, nrForCallback.getRequestorPackageName(),
+                                netCap, includeLocationSensitiveInfo, nri.mPid, nri.mUid,
+                                nrForCallback.getRequestorPackageName(),
                                 nri.mCallingAttributionTag));
                 break;
             }
@@ -7738,6 +8004,7 @@
                 updateCapabilitiesForNetwork(networkAgent);
             }
             networkAgent.created = true;
+            networkAgent.onNetworkCreated();
         }
 
         if (!networkAgent.everConnected && state == NetworkInfo.State.CONNECTED) {
@@ -7806,7 +8073,7 @@
         }
     }
 
-    private void updateNetworkScore(@NonNull final NetworkAgentInfo nai, final int score) {
+    private void updateNetworkScore(@NonNull final NetworkAgentInfo nai, final NetworkScore score) {
         if (VDBG || DDBG) log("updateNetworkScore for " + nai.toShortString() + " to " + score);
         nai.setScore(score);
         rematchAllNetworksAndRequests();
@@ -7825,12 +8092,11 @@
             return;
         }
 
+        final int blockedReasons = mUidBlockedReasons.get(nri.mAsUid, BLOCKED_REASON_NONE);
         final boolean metered = nai.networkCapabilities.isMetered();
-        boolean blocked;
-        blocked = isUidBlockedByVpn(nri.mUid, mVpnBlockedUidRanges);
-        blocked |= isUidBlockedByRules(nri.mUid, mUidRules.get(nri.mUid),
-                metered, mRestrictBackground);
-        callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, blocked ? 1 : 0);
+        final boolean vpnBlocked = isUidBlockedByVpn(nri.mAsUid, mVpnBlockedUidRanges);
+        callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE,
+                getBlockedState(blockedReasons, metered, vpnBlocked));
     }
 
     // Notify the requests on this NAI that the network is now lingered.
@@ -7839,6 +8105,21 @@
         notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
     }
 
+    private static int getBlockedState(int reasons, boolean metered, boolean vpnBlocked) {
+        if (!metered) reasons &= ~BLOCKED_METERED_REASON_MASK;
+        return vpnBlocked
+                ? reasons | BLOCKED_REASON_LOCKDOWN_VPN
+                : reasons & ~BLOCKED_REASON_LOCKDOWN_VPN;
+    }
+
+    private void setUidBlockedReasons(int uid, @BlockedReason int blockedReasons) {
+        if (blockedReasons == BLOCKED_REASON_NONE) {
+            mUidBlockedReasons.delete(uid);
+        } else {
+            mUidBlockedReasons.put(uid, blockedReasons);
+        }
+    }
+
     /**
      * Notify of the blocked state apps with a registered callback matching a given NAI.
      *
@@ -7846,59 +8127,56 @@
      * any given nai, all requests need to be considered according to the uid who filed it.
      *
      * @param nai The target NetworkAgentInfo.
-     * @param oldMetered True if the previous network capabilities is metered.
-     * @param newRestrictBackground True if data saver is enabled.
+     * @param oldMetered True if the previous network capabilities were metered.
+     * @param newMetered True if the current network capabilities are metered.
+     * @param oldBlockedUidRanges list of UID ranges previously blocked by lockdown VPN.
+     * @param newBlockedUidRanges list of UID ranges blocked by lockdown VPN.
      */
     private void maybeNotifyNetworkBlocked(NetworkAgentInfo nai, boolean oldMetered,
-            boolean newMetered, boolean oldRestrictBackground, boolean newRestrictBackground,
-            List<UidRange> oldBlockedUidRanges, List<UidRange> newBlockedUidRanges) {
+            boolean newMetered, List<UidRange> oldBlockedUidRanges,
+            List<UidRange> newBlockedUidRanges) {
 
         for (int i = 0; i < nai.numNetworkRequests(); i++) {
             NetworkRequest nr = nai.requestAt(i);
             NetworkRequestInfo nri = mNetworkRequests.get(nr);
-            final int uidRules = mUidRules.get(nri.mUid);
-            final boolean oldBlocked, newBlocked, oldVpnBlocked, newVpnBlocked;
 
-            oldVpnBlocked = isUidBlockedByVpn(nri.mUid, oldBlockedUidRanges);
-            newVpnBlocked = (oldBlockedUidRanges != newBlockedUidRanges)
-                    ? isUidBlockedByVpn(nri.mUid, newBlockedUidRanges)
+            final int blockedReasons = mUidBlockedReasons.get(nri.mAsUid, BLOCKED_REASON_NONE);
+            final boolean oldVpnBlocked = isUidBlockedByVpn(nri.mAsUid, oldBlockedUidRanges);
+            final boolean newVpnBlocked = (oldBlockedUidRanges != newBlockedUidRanges)
+                    ? isUidBlockedByVpn(nri.mAsUid, newBlockedUidRanges)
                     : oldVpnBlocked;
 
-            oldBlocked = oldVpnBlocked || isUidBlockedByRules(nri.mUid, uidRules, oldMetered,
-                    oldRestrictBackground);
-            newBlocked = newVpnBlocked || isUidBlockedByRules(nri.mUid, uidRules, newMetered,
-                    newRestrictBackground);
-
-            if (oldBlocked != newBlocked) {
+            final int oldBlockedState = getBlockedState(blockedReasons, oldMetered, oldVpnBlocked);
+            final int newBlockedState = getBlockedState(blockedReasons, newMetered, newVpnBlocked);
+            if (oldBlockedState != newBlockedState) {
                 callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED,
-                        encodeBool(newBlocked));
+                        newBlockedState);
             }
         }
     }
 
     /**
-     * Notify apps with a given UID of the new blocked state according to new uid rules.
+     * Notify apps with a given UID of the new blocked state according to new uid state.
      * @param uid The uid for which the rules changed.
-     * @param newRules The new rules to apply.
+     * @param blockedReasons The reasons for why an uid is blocked.
      */
-    private void maybeNotifyNetworkBlockedForNewUidRules(int uid, int newRules) {
+    private void maybeNotifyNetworkBlockedForNewState(int uid, @BlockedReason int blockedReasons) {
         for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
             final boolean metered = nai.networkCapabilities.isMetered();
             final boolean vpnBlocked = isUidBlockedByVpn(uid, mVpnBlockedUidRanges);
-            final boolean oldBlocked, newBlocked;
-            oldBlocked = vpnBlocked || isUidBlockedByRules(
-                    uid, mUidRules.get(uid), metered, mRestrictBackground);
-            newBlocked = vpnBlocked || isUidBlockedByRules(
-                    uid, newRules, metered, mRestrictBackground);
-            if (oldBlocked == newBlocked) {
+
+            final int oldBlockedState = getBlockedState(
+                    mUidBlockedReasons.get(uid, BLOCKED_REASON_NONE), metered, vpnBlocked);
+            final int newBlockedState = getBlockedState(blockedReasons, metered, vpnBlocked);
+            if (oldBlockedState == newBlockedState) {
                 continue;
             }
-            final int arg = encodeBool(newBlocked);
             for (int i = 0; i < nai.numNetworkRequests(); i++) {
                 NetworkRequest nr = nai.requestAt(i);
                 NetworkRequestInfo nri = mNetworkRequests.get(nr);
-                if (nri != null && nri.mUid == uid) {
-                    callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED, arg);
+                if (nri != null && nri.mAsUid == uid) {
+                    callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED,
+                            newBlockedState);
                 }
             }
         }
@@ -7914,6 +8192,7 @@
         // and is still connected.
         NetworkInfo info = new NetworkInfo(nai.networkInfo);
         info.setType(type);
+        filterForLegacyLockdown(info);
         if (state != DetailedState.DISCONNECTED) {
             info.setDetailedState(state, null, info.getExtraInfo());
             sendConnectedBroadcast(info);
@@ -8028,15 +8307,15 @@
     @Override
     public String getCaptivePortalServerUrl() {
         enforceNetworkStackOrSettingsPermission();
-        String settingUrl = mContext.getResources().getString(
-                R.string.config_networkCaptivePortalServerUrl);
+        String settingUrl = mResources.get().getString(
+                com.android.connectivity.resources.R.string.config_networkCaptivePortalServerUrl);
 
         if (!TextUtils.isEmpty(settingUrl)) {
             return settingUrl;
         }
 
         settingUrl = Settings.Global.getString(mContext.getContentResolver(),
-                Settings.Global.CAPTIVE_PORTAL_HTTP_URL);
+                ConnectivitySettingsManager.CAPTIVE_PORTAL_HTTP_URL);
         if (!TextUtils.isEmpty(settingUrl)) {
             return settingUrl;
         }
@@ -8118,11 +8397,11 @@
         // restore private DNS settings to default mode (opportunistic)
         if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_PRIVATE_DNS)) {
             Settings.Global.putString(mContext.getContentResolver(),
-                    Settings.Global.PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_OPPORTUNISTIC);
+                    ConnectivitySettingsManager.PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_OPPORTUNISTIC);
         }
 
         Settings.Global.putString(mContext.getContentResolver(),
-                Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
+                ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, null);
     }
 
     @Override
@@ -8235,7 +8514,7 @@
 
         final NetworkAgentInfo vpn = getVpnForUid(uid);
         if (vpn == null || getVpnType(vpn) != VpnManager.TYPE_VPN_SERVICE
-                || vpn.networkCapabilities.getOwnerUid() != Binder.getCallingUid()) {
+                || vpn.networkCapabilities.getOwnerUid() != mDeps.getCallingUid()) {
             return INVALID_UID;
         }
 
@@ -8661,7 +8940,7 @@
         // nri is not bound to the death of callback. Instead, callback.bindToDeath() is set in
         // handleRegisterConnectivityDiagnosticsCallback(). nri will be cleaned up as part of the
         // callback's binder death.
-        final NetworkRequestInfo nri = new NetworkRequestInfo(requestWithId);
+        final NetworkRequestInfo nri = new NetworkRequestInfo(callingUid, requestWithId);
         final ConnectivityDiagnosticsCallbackInfo cbInfo =
                 new ConnectivityDiagnosticsCallbackInfo(callback, nri, callingPackageName);
 
@@ -8827,13 +9106,13 @@
         private int transportTypeToLegacyType(int type) {
             switch (type) {
                 case NetworkCapabilities.TRANSPORT_CELLULAR:
-                    return ConnectivityManager.TYPE_MOBILE;
+                    return TYPE_MOBILE;
                 case NetworkCapabilities.TRANSPORT_WIFI:
-                    return ConnectivityManager.TYPE_WIFI;
+                    return TYPE_WIFI;
                 case NetworkCapabilities.TRANSPORT_BLUETOOTH:
-                    return ConnectivityManager.TYPE_BLUETOOTH;
+                    return TYPE_BLUETOOTH;
                 case NetworkCapabilities.TRANSPORT_ETHERNET:
-                    return ConnectivityManager.TYPE_ETHERNET;
+                    return TYPE_ETHERNET;
                 default:
                     loge("Unexpected transport in transportTypeToLegacyType: " + type);
             }
@@ -8874,13 +9153,13 @@
             if (networkAgent.networkCapabilities.hasTransport(
                     NetworkCapabilities.TRANSPORT_CELLULAR)) {
                 timeout = Settings.Global.getInt(mContext.getContentResolver(),
-                        Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE,
+                        ConnectivitySettingsManager.DATA_ACTIVITY_TIMEOUT_MOBILE,
                         10);
                 type = NetworkCapabilities.TRANSPORT_CELLULAR;
             } else if (networkAgent.networkCapabilities.hasTransport(
                     NetworkCapabilities.TRANSPORT_WIFI)) {
                 timeout = Settings.Global.getInt(mContext.getContentResolver(),
-                        Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI,
+                        ConnectivitySettingsManager.DATA_ACTIVITY_TIMEOUT_WIFI,
                         15);
                 type = NetworkCapabilities.TRANSPORT_WIFI;
             } else {
@@ -9048,6 +9327,143 @@
         mQosCallbackTracker.unregisterCallback(callback);
     }
 
+    // Network preference per-profile and OEM network preferences can't be set at the same
+    // time, because it is unclear what should happen if both preferences are active for
+    // one given UID. To make it possible, the stack would have to clarify what would happen
+    // in case both are active at the same time. The implementation may have to be adjusted
+    // to implement the resulting rules. For example, a priority could be defined between them,
+    // where the OEM preference would be considered less or more important than the enterprise
+    // preference ; this would entail implementing the priorities somehow, e.g. by doing
+    // UID arithmetic with UID ranges or passing a priority to netd so that the routing rules
+    // are set at the right level. Other solutions are possible, e.g. merging of the
+    // preferences for the relevant UIDs.
+    private static void throwConcurrentPreferenceException() {
+        throw new IllegalStateException("Can't set NetworkPreferenceForUser and "
+                + "set OemNetworkPreference at the same time");
+    }
+
+    /**
+     * Request that a user profile is put by default on a network matching a given preference.
+     *
+     * See the documentation for the individual preferences for a description of the supported
+     * behaviors.
+     *
+     * @param profile the profile concerned.
+     * @param preference the preference for this profile, as one of the PROFILE_NETWORK_PREFERENCE_*
+     *                   constants.
+     * @param listener an optional listener to listen for completion of the operation.
+     */
+    @Override
+    public void setProfileNetworkPreference(@NonNull final UserHandle profile,
+            @ConnectivityManager.ProfileNetworkPreference final int preference,
+            @Nullable final IOnCompleteListener listener) {
+        Objects.requireNonNull(profile);
+        PermissionUtils.enforceNetworkStackPermission(mContext);
+        if (DBG) {
+            log("setProfileNetworkPreference " + profile + " to " + preference);
+        }
+        if (profile.getIdentifier() < 0) {
+            throw new IllegalArgumentException("Must explicitly specify a user handle ("
+                    + "UserHandle.CURRENT not supported)");
+        }
+        final UserManager um;
+        try {
+            um = mContext.createContextAsUser(profile, 0 /* flags */)
+                    .getSystemService(UserManager.class);
+        } catch (IllegalStateException e) {
+            throw new IllegalArgumentException("Profile does not exist");
+        }
+        if (!um.isManagedProfile()) {
+            throw new IllegalArgumentException("Profile must be a managed profile");
+        }
+        // Strictly speaking, mOemNetworkPreferences should only be touched on the
+        // handler thread. However it is an immutable object, so reading the reference is
+        // safe - it's just possible the value is slightly outdated. For the final check,
+        // see #handleSetProfileNetworkPreference. But if this can be caught here it is a
+        // lot easier to understand, so opportunistically check it.
+        if (!mOemNetworkPreferences.isEmpty()) {
+            throwConcurrentPreferenceException();
+        }
+        final NetworkCapabilities nc;
+        switch (preference) {
+            case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT:
+                nc = null;
+                break;
+            case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE:
+                final UidRange uids = UidRange.createForUser(profile);
+                nc = createDefaultNetworkCapabilitiesForUidRange(uids);
+                nc.addCapability(NET_CAPABILITY_ENTERPRISE);
+                nc.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
+                break;
+            default:
+                throw new IllegalArgumentException(
+                        "Invalid preference in setProfileNetworkPreference");
+        }
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_PROFILE_NETWORK_PREFERENCE,
+                new Pair<>(new ProfileNetworkPreferences.Preference(profile, nc), listener)));
+    }
+
+    private void validateNetworkCapabilitiesOfProfileNetworkPreference(
+            @Nullable final NetworkCapabilities nc) {
+        if (null == nc) return; // Null caps are always allowed. It means to remove the setting.
+        ensureRequestableCapabilities(nc);
+    }
+
+    private ArraySet<NetworkRequestInfo> createNrisFromProfileNetworkPreferences(
+            @NonNull final ProfileNetworkPreferences prefs) {
+        final ArraySet<NetworkRequestInfo> result = new ArraySet<>();
+        for (final ProfileNetworkPreferences.Preference pref : prefs.preferences) {
+            // The NRI for a user should be comprised of two layers:
+            // - The request for the capabilities
+            // - The request for the default network, for fallback. Create an image of it to
+            //   have the correct UIDs in it (also a request can only be part of one NRI, because
+            //   of lookups in 1:1 associations like mNetworkRequests).
+            // Note that denying a fallback can be implemented simply by not adding the second
+            // request.
+            final ArrayList<NetworkRequest> nrs = new ArrayList<>();
+            nrs.add(createNetworkRequest(NetworkRequest.Type.REQUEST, pref.capabilities));
+            nrs.add(createDefaultRequest());
+            setNetworkRequestUids(nrs, UidRange.fromIntRanges(pref.capabilities.getUids()));
+            final NetworkRequestInfo nri = new NetworkRequestInfo(Process.myUid(), nrs);
+            result.add(nri);
+        }
+        return result;
+    }
+
+    private void handleSetProfileNetworkPreference(
+            @NonNull final ProfileNetworkPreferences.Preference preference,
+            @Nullable final IOnCompleteListener listener) {
+        // setProfileNetworkPreference and setOemNetworkPreference are mutually exclusive, in
+        // particular because it's not clear what preference should win in case both apply
+        // to the same app.
+        // The binder call has already checked this, but as mOemNetworkPreferences is only
+        // touched on the handler thread, it's theoretically not impossible that it has changed
+        // since.
+        if (!mOemNetworkPreferences.isEmpty()) {
+            // This may happen on a device with an OEM preference set when a user is removed.
+            // In this case, it's safe to ignore. In particular this happens in the tests.
+            loge("handleSetProfileNetworkPreference, but OEM network preferences not empty");
+            return;
+        }
+
+        validateNetworkCapabilitiesOfProfileNetworkPreference(preference.capabilities);
+
+        mProfileNetworkPreferences = mProfileNetworkPreferences.plus(preference);
+        final ArraySet<NetworkRequestInfo> nris =
+                createNrisFromProfileNetworkPreferences(mProfileNetworkPreferences);
+        replaceDefaultNetworkRequestsForPreference(nris);
+        // Finally, rematch.
+        rematchAllNetworksAndRequests();
+
+        if (null != listener) {
+            try {
+                listener.onComplete();
+            } catch (RemoteException e) {
+                loge("Listener for setProfileNetworkPreference has died");
+            }
+        }
+    }
+
     private void enforceAutomotiveDevice() {
         final boolean isAutomotiveDevice =
                 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
@@ -9066,17 +9482,26 @@
      * Calling this will overwrite the existing preference.
      *
      * @param preference {@link OemNetworkPreferences} The application network preference to be set.
-     * @param listener {@link ConnectivityManager.OnSetOemNetworkPreferenceListener} Listener used
+     * @param listener {@link ConnectivityManager.OnCompleteListener} Listener used
      * to communicate completion of setOemNetworkPreference();
      */
     @Override
     public void setOemNetworkPreference(
             @NonNull final OemNetworkPreferences preference,
-            @Nullable final IOnSetOemNetworkPreferenceListener listener) {
+            @Nullable final IOnCompleteListener listener) {
 
         enforceAutomotiveDevice();
         enforceOemNetworkPreferencesPermission();
 
+        if (!mProfileNetworkPreferences.isEmpty()) {
+            // Strictly speaking, mProfileNetworkPreferences should only be touched on the
+            // handler thread. However it is an immutable object, so reading the reference is
+            // safe - it's just possible the value is slightly outdated. For the final check,
+            // see #handleSetOemPreference. But if this can be caught here it is a
+            // lot easier to understand, so opportunistically check it.
+            throwConcurrentPreferenceException();
+        }
+
         Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
         validateOemNetworkPreferences(preference);
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_OEM_NETWORK_PREFERENCE,
@@ -9095,11 +9520,22 @@
 
     private void handleSetOemNetworkPreference(
             @NonNull final OemNetworkPreferences preference,
-            @Nullable final IOnSetOemNetworkPreferenceListener listener) {
+            @Nullable final IOnCompleteListener listener) {
         Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
         if (DBG) {
             log("set OEM network preferences :" + preference.toString());
         }
+        // setProfileNetworkPreference and setOemNetworkPreference are mutually exclusive, in
+        // particular because it's not clear what preference should win in case both apply
+        // to the same app.
+        // The binder call has already checked this, but as mOemNetworkPreferences is only
+        // touched on the handler thread, it's theoretically not impossible that it has changed
+        // since.
+        if (!mProfileNetworkPreferences.isEmpty()) {
+            logwtf("handleSetOemPreference, but per-profile network preferences not empty");
+            return;
+        }
+
         final ArraySet<NetworkRequestInfo> nris =
                 new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(preference);
         replaceDefaultNetworkRequestsForPreference(nris);
@@ -9159,7 +9595,7 @@
             }
             // Include this nri if it will be tracked by the new per-app default requests.
             final boolean isNriGoingToBeTracked =
-                    getDefaultRequestTrackingUid(nri.mUid) != mDefaultRequest;
+                    getDefaultRequestTrackingUid(nri.mAsUid) != mDefaultRequest;
             if (isNriGoingToBeTracked) {
                 defaultCallbackRequests.add(nri);
             }
@@ -9181,7 +9617,7 @@
         final ArraySet<NetworkRequestInfo> callbackRequestsToRegister = new ArraySet<>();
         for (final NetworkRequestInfo callbackRequest : perAppCallbackRequestsForUpdate) {
             final NetworkRequestInfo trackingNri =
-                    getDefaultRequestTrackingUid(callbackRequest.mUid);
+                    getDefaultRequestTrackingUid(callbackRequest.mAsUid);
 
             // If this nri is not being tracked, the change it back to an untracked nri.
             if (trackingNri == mDefaultRequest) {
@@ -9191,21 +9627,20 @@
                 continue;
             }
 
-            final String requestorPackageName =
-                    callbackRequest.mRequests.get(0).getRequestorPackageName();
+            final NetworkRequest request = callbackRequest.mRequests.get(0);
             callbackRequestsToRegister.add(new NetworkRequestInfo(
                     callbackRequest,
                     copyNetworkRequestsForUid(
-                            trackingNri.mRequests, callbackRequest.mUid, requestorPackageName)));
+                            trackingNri.mRequests, callbackRequest.mAsUid,
+                            callbackRequest.mUid, request.getRequestorPackageName())));
         }
         return callbackRequestsToRegister;
     }
 
     private static void setNetworkRequestUids(@NonNull final List<NetworkRequest> requests,
             @NonNull final Set<UidRange> uids) {
-        final Set<UidRange> ranges = new ArraySet<>(uids);
         for (final NetworkRequest req : requests) {
-            req.networkCapabilities.setUids(ranges);
+            req.networkCapabilities.setUids(UidRange.toIntRanges(uids));
         }
     }
 
@@ -9301,7 +9736,7 @@
                 ranges.add(new UidRange(uid, uid));
             }
             setNetworkRequestUids(requests, ranges);
-            return new NetworkRequestInfo(requests);
+            return new NetworkRequestInfo(Process.myUid(), requests);
         }
 
         private NetworkRequest createUnmeteredNetworkRequest() {
diff --git a/services/core/java/com/android/server/ConnectivityServiceInitializer.java b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
index b992208..2465479 100644
--- a/services/core/java/com/android/server/ConnectivityServiceInitializer.java
+++ b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
@@ -16,9 +16,6 @@
 
 package com.android.server;
 
-import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
-import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
-
 import android.content.Context;
 import android.util.Log;
 
@@ -42,6 +39,6 @@
     public void onStart() {
         Log.i(TAG, "Registering " + Context.CONNECTIVITY_SERVICE);
         publishBinderService(Context.CONNECTIVITY_SERVICE, mConnectivity,
-                /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
+                /* allowIsolated= */ false);
     }
 }
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 81d4b9d..794cb93 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -56,6 +56,7 @@
 import android.system.OsConstants;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Range;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -756,13 +757,9 @@
         }
     }
 
-    // These values have been reserved in NetIdManager
-    @VisibleForTesting static final int TUN_INTF_NETID_START = 0xFC00;
-
-    public static final int TUN_INTF_NETID_RANGE = 0x0400;
-
     private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
-    private int mNextTunnelNetIdIndex = 0;
+    final Range<Integer> mNetIdRange = ConnectivityManager.getIpSecNetIdRange();
+    private int mNextTunnelNetId = mNetIdRange.getLower();
 
     /**
      * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
@@ -775,11 +772,13 @@
      */
     @VisibleForTesting
     int reserveNetId() {
+        final int range = mNetIdRange.getUpper() - mNetIdRange.getLower() + 1;
         synchronized (mTunnelNetIds) {
-            for (int i = 0; i < TUN_INTF_NETID_RANGE; i++) {
-                int index = mNextTunnelNetIdIndex;
-                int netId = index + TUN_INTF_NETID_START;
-                if (++mNextTunnelNetIdIndex >= TUN_INTF_NETID_RANGE) mNextTunnelNetIdIndex = 0;
+            for (int i = 0; i < range; i++) {
+                final int netId = mNextTunnelNetId;
+                if (++mNextTunnelNetId > mNetIdRange.getUpper()) {
+                    mNextTunnelNetId = mNetIdRange.getLower();
+                }
                 if (!mTunnelNetIds.get(netId)) {
                     mTunnelNetIds.put(netId, true);
                     return netId;
@@ -1652,7 +1651,7 @@
                         c.getMode(),
                         c.getSourceAddress(),
                         c.getDestinationAddress(),
-                        (c.getNetwork() != null) ? c.getNetwork().netId : 0,
+                        (c.getNetwork() != null) ? c.getNetwork().getNetId() : 0,
                         spiRecord.getSpi(),
                         c.getMarkValue(),
                         c.getMarkMask(),
diff --git a/services/core/java/com/android/server/NetIdManager.java b/services/core/java/com/android/server/NetIdManager.java
index 097fb3a..61925c8 100644
--- a/services/core/java/com/android/server/NetIdManager.java
+++ b/services/core/java/com/android/server/NetIdManager.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.annotation.NonNull;
+import android.net.ConnectivityManager;
 import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -31,7 +32,7 @@
     // Sequence number for Networks; keep in sync with system/netd/NetworkController.cpp
     public static final int MIN_NET_ID = 100; // some reserved marks
     // Top IDs reserved by IpSecService
-    public static final int MAX_NET_ID = 65535 - IpSecService.TUN_INTF_NETID_RANGE;
+    public static final int MAX_NET_ID = ConnectivityManager.getIpSecNetIdRange().getLower() - 1;
 
     @GuardedBy("mNetIdInUse")
     private final SparseBooleanArray mNetIdInUse = new SparseBooleanArray();
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 10d6570..3ea0ce1 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -643,7 +643,7 @@
                 String route, String gateway, String ifName) throws RemoteException {
             final RouteInfo processRoute = new RouteInfo(new IpPrefix(route),
                     ("".equals(gateway)) ? null : InetAddresses.parseNumericAddress(gateway),
-                    ifName);
+                    ifName, RouteInfo.RTN_UNICAST);
             mDaemonHandler.post(() -> notifyRouteChange(updated, processRoute));
         }
 
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 222c96f..3bcde12 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -19,6 +19,7 @@
 
 per-file *Alarm* = file:/apex/jobscheduler/OWNERS
 per-file *AppOp* = file:/core/java/android/permission/OWNERS
+per-file *Battery* = file:/BATTERY_STATS_OWNERS
 per-file *Bluetooth* = file:/core/java/android/bluetooth/OWNERS
 per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
 per-file *Location* = file:/services/core/java/com/android/server/location/OWNERS
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 00d8b0f..351e616 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
+import android.os.FileUtils;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemProperties;
@@ -103,6 +104,9 @@
 public class PersistentDataBlockService extends SystemService {
     private static final String TAG = PersistentDataBlockService.class.getSimpleName();
 
+    private static final String GSI_SANDBOX = "/data/gsi_persistent_data";
+    private static final String GSI_RUNNING_PROP = "ro.gsid.image_running";
+
     private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
     private static final int HEADER_SIZE = 8;
     // Magic number to mark block device as adhering to the format consumed by this service
@@ -127,12 +131,13 @@
     private static final String FLASH_LOCK_UNLOCKED = "0";
 
     private final Context mContext;
-    private final String mDataBlockFile;
+    private final boolean mIsRunningDSU;
     private final Object mLock = new Object();
     private final CountDownLatch mInitDoneSignal = new CountDownLatch(1);
 
     private int mAllowedUid = -1;
     private long mBlockDeviceSize;
+    private String mDataBlockFile;
 
     @GuardedBy("mLock")
     private boolean mIsWritable = true;
@@ -141,6 +146,7 @@
         super(context);
         mContext = context;
         mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
+        mIsRunningDSU = SystemProperties.getBoolean(GSI_RUNNING_PROP, false);
         mBlockDeviceSize = -1; // Load lazily
     }
 
@@ -284,14 +290,28 @@
         return true;
     }
 
+    private FileOutputStream getBlockOutputStream() throws IOException {
+        if (!mIsRunningDSU) {
+            return new FileOutputStream(new File(mDataBlockFile));
+        } else {
+            File sandbox = new File(GSI_SANDBOX);
+            File realpdb = new File(SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP));
+            if (!sandbox.exists()) {
+                FileUtils.copy(realpdb, sandbox);
+                mDataBlockFile = GSI_SANDBOX;
+            }
+            Slog.i(TAG, "PersistentDataBlock copy-on-write");
+            return new FileOutputStream(sandbox);
+        }
+    }
+
     private boolean computeAndWriteDigestLocked() {
         byte[] digest = computeDigestLocked(null);
         if (digest != null) {
             DataOutputStream outputStream;
             try {
-                outputStream = new DataOutputStream(
-                        new FileOutputStream(new File(mDataBlockFile)));
-            } catch (FileNotFoundException e) {
+                outputStream = new DataOutputStream(getBlockOutputStream());
+            } catch (IOException e) {
                 Slog.e(TAG, "partition not available?", e);
                 return false;
             }
@@ -356,8 +376,8 @@
     private void formatPartitionLocked(boolean setOemUnlockEnabled) {
         DataOutputStream outputStream;
         try {
-            outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
-        } catch (FileNotFoundException e) {
+            outputStream = new DataOutputStream(getBlockOutputStream());
+        } catch (IOException e) {
             Slog.e(TAG, "partition not available?", e);
             return;
         }
@@ -382,8 +402,8 @@
     private void doSetOemUnlockEnabledLocked(boolean enabled) {
         FileOutputStream outputStream;
         try {
-            outputStream = new FileOutputStream(new File(mDataBlockFile));
-        } catch (FileNotFoundException e) {
+            outputStream = getBlockOutputStream();
+        } catch (IOException e) {
             Slog.e(TAG, "partition not available", e);
             return;
         }
@@ -459,8 +479,8 @@
 
             DataOutputStream outputStream;
             try {
-                outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
-            } catch (FileNotFoundException e) {
+                outputStream = new DataOutputStream(getBlockOutputStream());
+            } catch (IOException e) {
                 Slog.e(TAG, "partition not available?", e);
                 return -1;
             }
@@ -545,6 +565,17 @@
         public void wipe() {
             enforceOemUnlockWritePermission();
 
+            if (mIsRunningDSU) {
+                File sandbox = new File(GSI_SANDBOX);
+                if (sandbox.exists()) {
+                    if (sandbox.delete()) {
+                        mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
+                    } else {
+                        Slog.e(TAG, "Failed to wipe sandbox persistent data block");
+                    }
+                }
+                return;
+            }
             synchronized (mLock) {
                 int ret = nativeWipe(mDataBlockFile);
 
@@ -704,8 +735,8 @@
         private void writeDataBuffer(long offset, ByteBuffer dataBuffer) {
             FileOutputStream outputStream;
             try {
-                outputStream = new FileOutputStream(new File(mDataBlockFile));
-            } catch (FileNotFoundException e) {
+                outputStream = getBlockOutputStream();
+            } catch (IOException e) {
                 Slog.e(TAG, "partition not available", e);
                 return;
             }
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 3148a62..1241b7779 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -270,18 +270,9 @@
      * Handler for on start pinning message
      */
     private void handlePinOnStart() {
-        final String bootImage = SystemProperties.get("dalvik.vm.boot-image", "");
-        String[] filesToPin = null;
-        if (bootImage.endsWith("boot-image.prof")) {
-            // Use the files listed for that specific boot image.
-            // TODO: find a better way to know we're using the JIT zygote configuration.
-            filesToPin = mContext.getResources().getStringArray(
-                  com.android.internal.R.array.config_jitzygoteBootImagePinnerServiceFiles);
-        } else {
-            // Files to pin come from the overlay and can be specified per-device config
-            filesToPin = mContext.getResources().getStringArray(
-                  com.android.internal.R.array.config_defaultPinnerServiceFiles);
-        }
+        // Files to pin come from the overlay and can be specified per-device config
+        String[] filesToPin = mContext.getResources().getStringArray(
+            com.android.internal.R.array.config_defaultPinnerServiceFiles);
         // Continue trying to pin each file even if we fail to pin some of them
         for (String fileToPin : filesToPin) {
             PinnedFile pf = pinFile(fileToPin,
@@ -291,10 +282,32 @@
                 Slog.e(TAG, "Failed to pin file = " + fileToPin);
                 continue;
             }
-
             synchronized (this) {
                 mPinnedFiles.add(pf);
             }
+            if (fileToPin.endsWith(".jar") | fileToPin.endsWith(".apk")) {
+                // Check whether the runtime has compilation artifacts to pin.
+                String arch = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
+                String[] files = null;
+                try {
+                    files = DexFile.getDexFileOutputPaths(fileToPin, arch);
+                } catch (IOException ioe) { }
+                if (files == null) {
+                    continue;
+                }
+                for (String file : files) {
+                    PinnedFile df = pinFile(file,
+                                            Integer.MAX_VALUE,
+                                            /*attemptPinIntrospection=*/false);
+                    if (df == null) {
+                        Slog.i(TAG, "Failed to pin ART file = " + file);
+                        continue;
+                    }
+                    synchronized (this) {
+                        mPinnedFiles.add(df);
+                    }
+                }
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index dce919d..a95589b 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -60,6 +60,7 @@
 import android.telephony.CellSignalStrengthTdscdma;
 import android.telephony.CellSignalStrengthWcdma;
 import android.telephony.DisconnectCause;
+import android.telephony.LinkCapacityEstimate;
 import android.telephony.LocationAccessPolicy;
 import android.telephony.PhoneCapability;
 import android.telephony.PhoneStateListener;
@@ -318,7 +319,10 @@
 
     private int[] mDataEnabledReason;
 
-    private Map<Integer, Long> mAllowedNetworkTypesList;
+    private int[] mAllowedNetworkTypeReason;
+    private long[] mAllowedNetworkTypeValue;
+
+    private List<List<LinkCapacityEstimate>> mLinkCapacityEstimateLists;
 
     /**
      * Per-phone map of precise data connection state. The key of the map is the pair of transport
@@ -350,6 +354,8 @@
                 TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
         REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
                 TelephonyCallback.EVENT_DATA_ENABLED_CHANGED);
+        REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
+                TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED);
     }
 
     private boolean isLocationPermissionRequired(Set<Integer> events) {
@@ -383,7 +389,8 @@
     private boolean isPrivilegedPhoneStatePermissionRequired(Set<Integer> events) {
         return events.contains(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED)
                 || events.contains(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
-                || events.contains(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED);
+                || events.contains(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED)
+                || events.contains(TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED);
     }
 
     private static final int MSG_USER_SWITCHED = 1;
@@ -527,6 +534,8 @@
         mTelephonyDisplayInfos = copyOf(mTelephonyDisplayInfos, mNumPhones);
         mIsDataEnabled= copyOf(mIsDataEnabled, mNumPhones);
         mDataEnabledReason = copyOf(mDataEnabledReason, mNumPhones);
+        mAllowedNetworkTypeReason = copyOf(mAllowedNetworkTypeReason, mNumPhones);
+        mAllowedNetworkTypeValue = copyOf(mAllowedNetworkTypeValue, mNumPhones);
 
         // ds -> ss switch.
         if (mNumPhones < oldNumPhones) {
@@ -535,6 +544,7 @@
             cutListToSize(mPreciseDataConnectionStates, mNumPhones);
             cutListToSize(mBarringInfo, mNumPhones);
             cutListToSize(mPhysicalChannelConfigs, mNumPhones);
+            cutListToSize(mLinkCapacityEstimateLists, mNumPhones);
             return;
         }
 
@@ -571,6 +581,9 @@
             mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build());
             mIsDataEnabled[i] = false;
             mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
+            mAllowedNetworkTypeReason[i] = -1;
+            mAllowedNetworkTypeValue[i] = -1;
+            mLinkCapacityEstimateLists.add(i, new ArrayList<>());
         }
     }
 
@@ -630,9 +643,12 @@
         mBarringInfo = new ArrayList<>();
         mTelephonyDisplayInfos = new TelephonyDisplayInfo[numPhones];
         mPhysicalChannelConfigs = new ArrayList<>();
+        mAllowedNetworkTypeReason = new int[numPhones];
+        mAllowedNetworkTypeValue = new long[numPhones];
         mIsDataEnabled = new boolean[numPhones];
         mDataEnabledReason = new int[numPhones];
-        mAllowedNetworkTypesList = new HashMap<>();
+        mLinkCapacityEstimateLists = new ArrayList<>();
+
         for (int i = 0; i < numPhones; i++) {
             mCallState[i] =  TelephonyManager.CALL_STATE_IDLE;
             mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE;
@@ -665,6 +681,9 @@
             mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build());
             mIsDataEnabled[i] = false;
             mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
+            mAllowedNetworkTypeReason[i] = -1;
+            mAllowedNetworkTypeValue[i] = -1;
+            mLinkCapacityEstimateLists.add(i, new ArrayList<>());
         }
 
         mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -1151,7 +1170,9 @@
                         TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)) {
                     try {
                         r.callback.onPhysicalChannelConfigChanged(
-                                mPhysicalChannelConfigs);
+                                shouldSanitizeLocationForPhysicalChannelConfig(r)
+                                        ? getLocationSanitizedConfigs(mPhysicalChannelConfigs)
+                                        : mPhysicalChannelConfigs);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
@@ -1166,9 +1187,12 @@
                     }
                 }
                 if (events.contains(
-                        TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)) {
+                        TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED)) {
                     try {
-                        r.callback.onAllowedNetworkTypesChanged(mAllowedNetworkTypesList);
+                        if (mLinkCapacityEstimateLists.get(phoneId) != null) {
+                            r.callback.onLinkCapacityEstimateChanged(mLinkCapacityEstimateLists
+                                    .get(phoneId));
+                        }
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
@@ -2349,8 +2373,10 @@
             return;
         }
 
+        List<PhysicalChannelConfig> sanitizedConfigs = getLocationSanitizedConfigs(configs);
         if (VDBG) {
-            log("notifyPhysicalChannelConfig: subId=" + subId + " configs=" + configs);
+            log("notifyPhysicalChannelConfig: subId=" + subId + " configs=" + configs
+                    + " sanitizedConfigs=" + sanitizedConfigs);
         }
 
         synchronized (mRecords) {
@@ -2363,11 +2389,14 @@
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             if (DBG_LOC) {
-                                log("notifyPhysicalChannelConfig: "
-                                        + "mPhysicalChannelConfigs="
-                                        + configs + " r=" + r);
+                                log("notifyPhysicalChannelConfig: mPhysicalChannelConfigs="
+                                        + (shouldSanitizeLocationForPhysicalChannelConfig(r)
+                                                ? sanitizedConfigs : configs)
+                                        + " r=" + r);
                             }
-                            r.callback.onPhysicalChannelConfigChanged(configs);
+                            r.callback.onPhysicalChannelConfigChanged(
+                                    shouldSanitizeLocationForPhysicalChannelConfig(r)
+                                            ? sanitizedConfigs : configs);
                         } catch (RemoteException ex) {
                             mRemoveList.add(r.binder);
                         }
@@ -2378,6 +2407,25 @@
         }
     }
 
+    private static boolean shouldSanitizeLocationForPhysicalChannelConfig(Record record) {
+        // Always redact location info from PhysicalChannelConfig if the registrant is from neither
+        // PHONE nor SYSTEM process. There is no user case that the registrant needs the location
+        // info (e.g. physicalCellId). This also remove the need for the location permissions check.
+        return record.callerUid != Process.PHONE_UID && record.callerUid != Process.SYSTEM_UID;
+    }
+
+    /**
+     * Return a copy of the PhysicalChannelConfig list but with location info removed.
+     */
+    private static List<PhysicalChannelConfig> getLocationSanitizedConfigs(
+            List<PhysicalChannelConfig> configs) {
+        List<PhysicalChannelConfig> sanitizedConfigs = new ArrayList<>(configs.size());
+        for (PhysicalChannelConfig config : configs) {
+            sanitizedConfigs.add(config.createLocationInfoSanitizedCopy());
+        }
+        return sanitizedConfigs;
+    }
+
     /**
      * Notify that the data enabled has changed.
      *
@@ -2423,18 +2471,19 @@
      *
      * @param phoneId the phone id.
      * @param subId the subId.
-     * @param allowedNetworkTypesList Map associating all allowed network type reasons with reason's
-     *                                allowed network type values.
+     * @param reason the allowed network type reason.
+     * @param allowedNetworkType the allowed network type value.
      */
-    public void notifyAllowedNetworkTypesChanged(int phoneId, int subId,
-            Map allowedNetworkTypesList) {
+    public void notifyAllowedNetworkTypesChanged(int phoneId, int subId, int reason,
+            long allowedNetworkType) {
         if (!checkNotifyPermission("notifyAllowedNetworkTypesChanged()")) {
             return;
         }
 
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
-                mAllowedNetworkTypesList = allowedNetworkTypesList;
+                mAllowedNetworkTypeReason[phoneId] = reason;
+                mAllowedNetworkTypeValue[phoneId] = allowedNetworkType;
 
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
@@ -2442,10 +2491,48 @@
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             if (VDBG) {
-                                log("notifyAllowedNetworkTypesChanged: AllowedNetworkTypesList= "
-                                        + mAllowedNetworkTypesList.toString());
+                                log("notifyAllowedNetworkTypesChanged: reason= " + reason
+                                        + ", allowed network type:"
+                                        + TelephonyManager.convertNetworkTypeBitmaskToString(
+                                        allowedNetworkType));
                             }
-                            r.callback.onAllowedNetworkTypesChanged(mAllowedNetworkTypesList);
+                            r.callback.onAllowedNetworkTypesChanged(reason, allowedNetworkType);
+                        } catch (RemoteException ex) {
+                            mRemoveList.add(r.binder);
+                        }
+                    }
+                }
+            }
+            handleRemoveListLocked();
+        }
+    }
+
+    /**
+     * Notify that the link capacity estimate has changed.
+     * @param phoneId the phone id.
+     * @param subId the subscription id.
+     * @param linkCapacityEstimateList a list of {@link LinkCapacityEstimate}
+     */
+    public void notifyLinkCapacityEstimateChanged(int phoneId, int subId,
+            List<LinkCapacityEstimate> linkCapacityEstimateList) {
+        if (!checkNotifyPermission("notifyLinkCapacityEstimateChanged()")) {
+            return;
+        }
+
+        if (VDBG) {
+            log("notifyLinkCapacityEstimateChanged: linkCapacityEstimateList ="
+                    + linkCapacityEstimateList);
+        }
+
+        synchronized (mRecords) {
+            if (validatePhoneId(phoneId)) {
+                mLinkCapacityEstimateLists.set(phoneId, linkCapacityEstimateList);
+                for (Record r : mRecords) {
+                    if (r.matchTelephonyCallbackEvent(
+                            TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED)
+                            && idMatch(r.subId, subId, phoneId)) {
+                        try {
+                            r.callback.onLinkCapacityEstimateChanged(linkCapacityEstimateList);
                         } catch (RemoteException ex) {
                             mRemoveList.add(r.binder);
                         }
@@ -2500,6 +2587,9 @@
                 pw.println("mTelephonyDisplayInfo=" + mTelephonyDisplayInfos[i]);
                 pw.println("mIsDataEnabled=" + mIsDataEnabled);
                 pw.println("mDataEnabledReason=" + mDataEnabledReason);
+                pw.println("mAllowedNetworkTypeReason=" + mAllowedNetworkTypeReason[i]);
+                pw.println("mAllowedNetworkTypeValue=" + mAllowedNetworkTypeValue[i]);
+                pw.println("mLinkCapacityEstimateList=" + mLinkCapacityEstimateLists.get(i));
                 pw.decreaseIndent();
             }
             pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState);
@@ -2764,6 +2854,7 @@
         LocationAccessPolicy.LocationPermissionQuery.Builder locationQueryBuilder =
                 new LocationAccessPolicy.LocationPermissionQuery.Builder()
                         .setCallingPackage(callingPackage)
+                        .setCallingFeatureId(callingFeatureId)
                         .setMethod(message + " events: " + events)
                         .setCallingPid(Binder.getCallingPid())
                         .setCallingUid(Binder.getCallingUid());
@@ -2913,6 +3004,7 @@
         LocationAccessPolicy.LocationPermissionQuery query =
                 new LocationAccessPolicy.LocationPermissionQuery.Builder()
                         .setCallingPackage(r.callingPackage)
+                        .setCallingFeatureId(r.callingFeatureId)
                         .setCallingPid(r.callerPid)
                         .setCallingUid(r.callerUid)
                         .setMethod("TelephonyRegistry push")
@@ -2936,6 +3028,7 @@
         LocationAccessPolicy.LocationPermissionQuery query =
                 new LocationAccessPolicy.LocationPermissionQuery.Builder()
                         .setCallingPackage(r.callingPackage)
+                        .setCallingFeatureId(r.callingFeatureId)
                         .setCallingPid(r.callerPid)
                         .setCallingUid(r.callerUid)
                         .setMethod("TelephonyRegistry push")
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index 55408ea..f566277 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -33,8 +33,8 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkProvider;
 import android.net.RouteInfo;
-import android.net.StringNetworkSpecifier;
 import android.net.TestNetworkInterface;
+import android.net.TestNetworkSpecifier;
 import android.net.util.NetdService;
 import android.os.Binder;
 import android.os.Handler;
@@ -90,7 +90,12 @@
         mCm = mContext.getSystemService(ConnectivityManager.class);
         mNetworkProvider = new NetworkProvider(mContext, mHandler.getLooper(),
                 TEST_NETWORK_PROVIDER_NAME);
-        mCm.registerNetworkProvider(mNetworkProvider);
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mCm.registerNetworkProvider(mNetworkProvider);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     /**
@@ -242,7 +247,7 @@
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
-        nc.setNetworkSpecifier(new StringNetworkSpecifier(iface));
+        nc.setNetworkSpecifier(new TestNetworkSpecifier(iface));
         nc.setAdministratorUids(administratorUids);
         if (!isMetered) {
             nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 502e74a..4622e98 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -29,7 +29,10 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.net.ConnectivityManager;
 import android.net.LinkProperties;
 import android.net.NetworkCapabilities;
@@ -64,7 +67,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
-import com.android.internal.util.LocationPermissionChecker;
+import com.android.net.module.util.LocationPermissionChecker;
 import com.android.server.vcn.TelephonySubscriptionTracker;
 import com.android.server.vcn.Vcn;
 import com.android.server.vcn.VcnContext;
@@ -158,6 +161,7 @@
     @NonNull private final TelephonySubscriptionTrackerCallback mTelephonySubscriptionTrackerCb;
     @NonNull private final TelephonySubscriptionTracker mTelephonySubscriptionTracker;
     @NonNull private final VcnContext mVcnContext;
+    @NonNull private final BroadcastReceiver mPkgChangeReceiver;
 
     /** Can only be assigned when {@link #systemReady()} is called, since it uses AppOpsManager. */
     @Nullable private LocationPermissionChecker mLocationPermissionChecker;
@@ -203,6 +207,29 @@
         mConfigDiskRwHelper = mDeps.newPersistableBundleLockingReadWriteHelper(VCN_CONFIG_FILE);
         mVcnContext = mDeps.newVcnContext(mContext, mLooper, mNetworkProvider);
 
+        mPkgChangeReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final String action = intent.getAction();
+
+                if (Intent.ACTION_PACKAGE_ADDED.equals(action)
+                        || Intent.ACTION_PACKAGE_REPLACED.equals(action)
+                        || Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                    mTelephonySubscriptionTracker.handleSubscriptionsChanged();
+                } else {
+                    Log.wtf(TAG, "received unexpected intent: " + action);
+                }
+            }
+        };
+
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        intentFilter.addDataScheme("package");
+        mContext.registerReceiver(
+                mPkgChangeReceiver, intentFilter, null /* broadcastPermission */, mHandler);
+
         // Run on handler to ensure I/O does not block system server startup
         mHandler.post(() -> {
             PersistableBundle configBundle = null;
@@ -349,7 +376,8 @@
         }
     }
 
-    private void enforceCallingUserAndCarrierPrivilege(ParcelUuid subscriptionGroup) {
+    private void enforceCallingUserAndCarrierPrivilege(
+            ParcelUuid subscriptionGroup, String pkgName) {
         // Only apps running in the primary (system) user are allowed to configure the VCN. This is
         // in line with Telephony's behavior with regards to binding to a Carrier App provided
         // CarrierConfigService.
@@ -363,12 +391,15 @@
                     subscriptionInfos.addAll(subMgr.getSubscriptionsInGroup(subscriptionGroup));
                 });
 
-        final TelephonyManager telMgr = mContext.getSystemService(TelephonyManager.class);
         for (SubscriptionInfo info : subscriptionInfos) {
+            final TelephonyManager telMgr = mContext.getSystemService(TelephonyManager.class)
+                    .createForSubscriptionId(info.getSubscriptionId());
+
             // Check subscription is active first; much cheaper/faster check, and an app (currently)
             // cannot be carrier privileged for inactive subscriptions.
             if (subMgr.isValidSlotIndex(info.getSimSlotIndex())
-                    && telMgr.hasCarrierPrivileges(info.getSubscriptionId())) {
+                    && telMgr.checkCarrierPrivilegesForPackage(pkgName)
+                            == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
                 // TODO (b/173717728): Allow configuration for inactive, but manageable
                 // subscriptions.
                 // TODO (b/173718661): Check for whole subscription groups at a time.
@@ -536,7 +567,7 @@
 
         mContext.getSystemService(AppOpsManager.class)
                 .checkPackage(mDeps.getBinderCallingUid(), config.getProvisioningPackageName());
-        enforceCallingUserAndCarrierPrivilege(subscriptionGroup);
+        enforceCallingUserAndCarrierPrivilege(subscriptionGroup, opPkgName);
 
         Binder.withCleanCallingIdentity(() -> {
             synchronized (mLock) {
@@ -554,11 +585,14 @@
      * <p>Implements the IVcnManagementService Binder interface.
      */
     @Override
-    public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) {
+    public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull String opPkgName) {
         requireNonNull(subscriptionGroup, "subscriptionGroup was null");
+        requireNonNull(opPkgName, "opPkgName was null");
         Slog.v(TAG, "VCN config cleared for subGrp: " + subscriptionGroup);
 
-        enforceCallingUserAndCarrierPrivilege(subscriptionGroup);
+        mContext.getSystemService(AppOpsManager.class)
+                .checkPackage(mDeps.getBinderCallingUid(), opPkgName);
+        enforceCallingUserAndCarrierPrivilege(subscriptionGroup, opPkgName);
 
         Binder.withCleanCallingIdentity(() -> {
             synchronized (mLock) {
@@ -667,6 +701,10 @@
             @NonNull IVcnUnderlyingNetworkPolicyListener listener) {
         requireNonNull(listener, "listener was null");
 
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.NETWORK_FACTORY,
+                "Must have permission NETWORK_FACTORY to unregister a policy listener");
+
         Binder.withCleanCallingIdentity(() -> {
             synchronized (mLock) {
                 PolicyListenerBinderDeath listenerBinderDeath =
@@ -817,8 +855,7 @@
 
             final IBinder cbBinder = callback.asBinder();
             final VcnStatusCallbackInfo cbInfo =
-                    new VcnStatusCallbackInfo(
-                            subGroup, callback, opPkgName, mDeps.getBinderCallingUid());
+                    new VcnStatusCallbackInfo(subGroup, callback, opPkgName, callingUid);
 
             try {
                 cbBinder.linkToDeath(cbInfo, 0 /* flags */);
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index 56aabc20..d756c1f 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -352,7 +352,10 @@
     @Override
     public void startLegacyVpn(VpnProfile profile) {
         int user = UserHandle.getUserId(mDeps.getCallingUid());
-        final LinkProperties egress = mCm.getActiveLinkProperties();
+        // Note that if the caller is not system (uid >= Process.FIRST_APPLICATION_UID),
+        // the code might not work well since getActiveNetwork might return null if the uid is
+        // blocked by NetworkPolicyManagerService.
+        final LinkProperties egress = mCm.getLinkProperties(mCm.getActiveNetwork());
         if (egress == null) {
             throw new IllegalStateException("Missing active network connection");
         }
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index a847847..a37115d 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -75,7 +75,8 @@
     //         can trigger the watchdog.
     // Note 2: The debug value is already below the wait time in ZygoteConnection. Wrapped
     //         applications may not work with a debug build. CTS will fail.
-    private static final long DEFAULT_TIMEOUT = DB ? 10 * 1000 : 60 * 1000;
+    private static final long DEFAULT_TIMEOUT =
+            (DB ? 10 * 1000 : 60 * 1000) * Build.HW_TIMEOUT_MULTIPLIER;
     private static final long CHECK_INTERVAL = DEFAULT_TIMEOUT / 2;
 
     // These are temporally ordered: larger values as lateness increases
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index dd0e1f6..ca4b9c3 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -140,14 +140,14 @@
     private static final int DEBUG_FGS_ENFORCE_TYPE = 1;
 
     // How long we wait for a service to finish executing.
-    static final int SERVICE_TIMEOUT = 20*1000;
+    static final int SERVICE_TIMEOUT = 20 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
 
     // How long we wait for a service to finish executing.
     static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
 
     // How long the startForegroundService() grace period is to get around to
     // calling startForeground() before we ANR + stop it.
-    static final int SERVICE_START_FOREGROUND_TIMEOUT = 10*1000;
+    static final int SERVICE_START_FOREGROUND_TIMEOUT = 10 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
 
     final ActivityManagerService mAm;
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index bd08b62..cc5a25a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -478,7 +478,7 @@
 
     // How long we wait for a launched process to attach to the activity manager
     // before we decide it's never going to come up for real.
-    static final int PROC_START_TIMEOUT = 10*1000;
+    static final int PROC_START_TIMEOUT = 10 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
     // How long we wait to kill an application zygote, after the last process using
     // it has gone away.
     static final int KILL_APP_ZYGOTE_DELAY_MS = 5 * 1000;
@@ -490,8 +490,8 @@
     static final int PROC_START_TIMEOUT_WITH_WRAPPER = 1200*1000;
 
     // How long we allow a receiver to run before giving up on it.
-    static final int BROADCAST_FG_TIMEOUT = 10*1000;
-    static final int BROADCAST_BG_TIMEOUT = 60*1000;
+    static final int BROADCAST_FG_TIMEOUT = 10 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
+    static final int BROADCAST_BG_TIMEOUT = 60 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
 
     public static final int MY_PID = myPid();
 
@@ -573,7 +573,8 @@
     private static final int MAX_BUGREPORT_TITLE_SIZE = 50;
     private static final int MAX_BUGREPORT_DESCRIPTION_SIZE = 150;
 
-    private static final int NATIVE_DUMP_TIMEOUT_MS = 2000; // 2 seconds;
+    private static final int NATIVE_DUMP_TIMEOUT_MS =
+            2000 * Build.HW_TIMEOUT_MULTIPLIER; // 2 seconds;
     private static final int JAVA_DUMP_MINIMUM_SIZE = 100; // 100 bytes.
 
     OomAdjuster mOomAdjuster;
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index be17b1b..54b3e64 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -18,6 +18,7 @@
 
 import android.content.ContentResolver;
 import android.database.ContentObserver;
+import android.os.Build;
 import android.os.Handler;
 import android.provider.Settings;
 import android.util.KeyValueListParser;
@@ -42,12 +43,13 @@
             "bcast_allow_bg_activity_start_timeout";
 
     // All time intervals are in milliseconds
-    private static final long DEFAULT_TIMEOUT = 10_000;
-    private static final long DEFAULT_SLOW_TIME = 5_000;
-    private static final long DEFAULT_DEFERRAL = 5_000;
+    private static final long DEFAULT_TIMEOUT = 10_000 * Build.HW_TIMEOUT_MULTIPLIER;
+    private static final long DEFAULT_SLOW_TIME = 5_000 * Build.HW_TIMEOUT_MULTIPLIER;
+    private static final long DEFAULT_DEFERRAL = 5_000 * Build.HW_TIMEOUT_MULTIPLIER;
     private static final float DEFAULT_DEFERRAL_DECAY_FACTOR = 0.75f;
     private static final long DEFAULT_DEFERRAL_FLOOR = 0;
-    private static final long DEFAULT_ALLOW_BG_ACTIVITY_START_TIMEOUT = 10_000;
+    private static final long DEFAULT_ALLOW_BG_ACTIVITY_START_TIMEOUT =
+            10_000 * Build.HW_TIMEOUT_MULTIPLIER;
 
     // All time constants are in milliseconds
 
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 1c38c86..90d9409 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -30,6 +30,10 @@
 michaelwr@google.com
 narayan@google.com
 
+# Voice Interaction
+per-file *Assist* = file:/core/java/android/service/voice/OWNERS
+per-file *Voice* = file:/core/java/android/service/voice/OWNERS
+
 per-file SettingsToPropertiesMapper.java = omakoto@google.com, svetoslavganov@google.com, yamasani@google.com
 
 per-file CarUserSwitchingDialog.java = keunyoung@google.com, felipeal@google.com, gurunagarajan@google.com
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationManagerInternal.java b/services/core/java/com/android/server/apphibernation/AppHibernationManagerInternal.java
new file mode 100644
index 0000000..b0335fe
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationManagerInternal.java
@@ -0,0 +1,46 @@
+/*
+ * 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.apphibernation;
+
+/**
+ * App hibernation manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class AppHibernationManagerInternal {
+
+    /**
+     * @see AppHibernationService#isHibernatingForUser
+     */
+    public abstract boolean isHibernatingForUser(String packageName, int userId);
+
+    /**
+     * @see AppHibernationService#setHibernatingForUser
+     */
+    public abstract void setHibernatingForUser(String packageName, int userId,
+            boolean isHibernating);
+
+    /**
+     * @see AppHibernationService#isHibernatingGlobally
+     */
+    public abstract boolean isHibernatingGlobally(String packageName);
+
+    /**
+     * @see AppHibernationService#setHibernatingGlobally
+     */
+    public abstract void setHibernatingGlobally(String packageName, boolean isHibernating);
+}
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 32ae878..351231f 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -59,6 +59,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
 import java.io.File;
@@ -68,6 +69,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 
@@ -100,6 +102,7 @@
     private final Map<String, GlobalLevelState> mGlobalHibernationStates = new ArrayMap<>();
     private final HibernationStateDiskStore<GlobalLevelState> mGlobalLevelHibernationDiskStore;
     private final Injector mInjector;
+    private final Executor mBackgroundExecutor;
 
     @VisibleForTesting
     boolean mIsServiceEnabled;
@@ -125,6 +128,7 @@
         mIActivityManager = injector.getActivityManager();
         mUserManager = injector.getUserManager();
         mGlobalLevelHibernationDiskStore = injector.getGlobalLevelDiskStore();
+        mBackgroundExecutor = injector.getBackgroundExecutor();
         mInjector = injector;
 
         final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
@@ -134,6 +138,8 @@
         intentFilter.addAction(ACTION_PACKAGE_REMOVED);
         intentFilter.addDataScheme("package");
         userAllContext.registerReceiver(mBroadcastReceiver, intentFilter);
+
+        LocalServices.addService(AppHibernationManagerInternal.class, mLocalService);
     }
 
     @Override
@@ -144,11 +150,13 @@
     @Override
     public void onBootPhase(int phase) {
         if (phase == PHASE_BOOT_COMPLETED) {
-            List<GlobalLevelState> states =
-                    mGlobalLevelHibernationDiskStore.readHibernationStates();
-            synchronized (mLock) {
-                initializeGlobalHibernationStates(states);
-            }
+            mBackgroundExecutor.execute(() -> {
+                List<GlobalLevelState> states =
+                        mGlobalLevelHibernationDiskStore.readHibernationStates();
+                synchronized (mLock) {
+                    initializeGlobalHibernationStates(states);
+                }
+            });
         }
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
             mIsServiceEnabled = isAppHibernationEnabled();
@@ -167,14 +175,15 @@
      * @return true if package is hibernating for the user
      */
     boolean isHibernatingForUser(String packageName, int userId) {
-        if (!checkHibernationEnabled("isHibernatingForUser")) {
+        String methodName = "isHibernatingForUser";
+        if (!checkHibernationEnabled(methodName)) {
             return false;
         }
-
-        userId = handleIncomingUser(userId, "isHibernating");
-        if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
-            Slog.e(TAG, "Attempt to get hibernation state of stopped or nonexistent user "
-                    + userId);
+        getContext().enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_APP_HIBERNATION,
+                "Caller does not have MANAGE_APP_HIBERNATION permission.");
+        userId = handleIncomingUser(userId, methodName);
+        if (!checkUserStatesExist(userId, methodName)) {
             return false;
         }
         synchronized (mLock) {
@@ -199,6 +208,9 @@
         if (!checkHibernationEnabled("isHibernatingGlobally")) {
             return false;
         }
+        getContext().enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_APP_HIBERNATION,
+                "Caller does not have MANAGE_APP_HIBERNATION permission.");
         synchronized (mLock) {
             GlobalLevelState state = mGlobalHibernationStates.get(packageName);
             if (state == null) {
@@ -217,13 +229,15 @@
      * @param isHibernating new hibernation state
      */
     void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
-        if (!checkHibernationEnabled("setHibernatingForUser")) {
+        String methodName = "setHibernatingForUser";
+        if (!checkHibernationEnabled(methodName)) {
             return;
         }
-        userId = handleIncomingUser(userId, "setHibernating");
-        if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
-            Slog.w(TAG, "Attempt to set hibernation state for a stopped or nonexistent user "
-                    + userId);
+        getContext().enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_APP_HIBERNATION,
+                "Caller does not have MANAGE_APP_HIBERNATION permission.");
+        userId = handleIncomingUser(userId, methodName);
+        if (!checkUserStatesExist(userId, methodName)) {
             return;
         }
         synchronized (mLock) {
@@ -260,6 +274,9 @@
         if (!checkHibernationEnabled("setHibernatingGlobally")) {
             return;
         }
+        getContext().enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_APP_HIBERNATION,
+                "Caller does not have MANAGE_APP_HIBERNATION permission.");
         synchronized (mLock) {
             GlobalLevelState state = mGlobalHibernationStates.get(packageName);
             if (state == null) {
@@ -279,6 +296,34 @@
     }
 
     /**
+     * Get the hibernating packages for the given user. This is equivalent to the list of
+     * packages for the user that return true for {@link #isHibernatingForUser}.
+     */
+    @NonNull List<String> getHibernatingPackagesForUser(int userId) {
+        ArrayList<String> hibernatingPackages = new ArrayList<>();
+        String methodName = "getHibernatingPackagesForUser";
+        if (!checkHibernationEnabled(methodName)) {
+            return hibernatingPackages;
+        }
+        getContext().enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_APP_HIBERNATION,
+                "Caller does not have MANAGE_APP_HIBERNATION permission.");
+        userId = handleIncomingUser(userId, methodName);
+        if (!checkUserStatesExist(userId, methodName)) {
+            return hibernatingPackages;
+        }
+        synchronized (mLock) {
+            Map<String, UserLevelState> userStates = mUserStates.get(userId);
+            for (UserLevelState state : userStates.values()) {
+                if (state.hibernated) {
+                    hibernatingPackages.add(state.packageName);
+                }
+            }
+            return hibernatingPackages;
+        }
+    }
+
+    /**
      * Put an app into hibernation for a given user, allowing user-level optimizations to occur.
      *
      * @param pkgState package hibernation state
@@ -434,10 +479,15 @@
         HibernationStateDiskStore<UserLevelState> diskStore =
                 mInjector.getUserLevelDiskStore(userId);
         mUserDiskStores.put(userId, diskStore);
-        List<UserLevelState> storedStates = diskStore.readHibernationStates();
-        synchronized (mLock) {
-            initializeUserHibernationStates(userId, storedStates);
-        }
+        mBackgroundExecutor.execute(() -> {
+            List<UserLevelState> storedStates = diskStore.readHibernationStates();
+            synchronized (mLock) {
+                // Ensure user hasn't stopped in the time to execute.
+                if (mUserManager.isUserUnlockingOrUnlocked(userId)) {
+                    initializeUserHibernationStates(userId, storedStates);
+                }
+            }
+        });
     }
 
     @Override
@@ -507,6 +557,20 @@
         }
     }
 
+    private boolean checkUserStatesExist(int userId, String methodName) {
+        if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
+            Slog.e(TAG, String.format(
+                    "Attempt to call %s on stopped or nonexistent user %d", methodName, userId));
+            return false;
+        }
+        if (!mUserStates.contains(userId)) {
+            Slog.w(TAG, String.format(
+                    "Attempt to call %s before states have been read from disk", methodName));
+            return false;
+        }
+        return true;
+    }
+
     private boolean checkHibernationEnabled(String methodName) {
         if (!mIsServiceEnabled) {
             Slog.w(TAG, String.format("Attempted to call %s on unsupported device.", methodName));
@@ -545,6 +609,36 @@
         }
     }
 
+    private final AppHibernationManagerInternal mLocalService = new LocalService(this);
+
+    private static final class LocalService extends AppHibernationManagerInternal {
+        private final AppHibernationService mService;
+
+        LocalService(AppHibernationService service) {
+            mService = service;
+        }
+
+        @Override
+        public boolean isHibernatingForUser(String packageName, int userId) {
+            return mService.isHibernatingForUser(packageName, userId);
+        }
+
+        @Override
+        public void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
+            mService.setHibernatingForUser(packageName, userId, isHibernating);
+        }
+
+        @Override
+        public void setHibernatingGlobally(String packageName, boolean isHibernating) {
+            mService.setHibernatingGlobally(packageName, isHibernating);
+        }
+
+        @Override
+        public boolean isHibernatingGlobally(String packageName) {
+            return mService.isHibernatingGlobally(packageName);
+        }
+    }
+
     private final AppHibernationServiceStub mServiceStub = new AppHibernationServiceStub(this);
 
     static final class AppHibernationServiceStub extends IAppHibernationService.Stub {
@@ -575,6 +669,11 @@
         }
 
         @Override
+        public List<String> getHibernatingPackagesForUser(int userId) {
+            return mService.getHibernatingPackagesForUser(userId);
+        }
+
+        @Override
         public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
                 @Nullable FileDescriptor err, @NonNull String[] args,
                 @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver) {
@@ -642,6 +741,8 @@
 
         UserManager getUserManager();
 
+        Executor getBackgroundExecutor();
+
         HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore();
 
         HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId);
@@ -680,6 +781,11 @@
         }
 
         @Override
+        public Executor getBackgroundExecutor() {
+            return mScheduledExecutorService;
+        }
+
+        @Override
         public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() {
             File dir = new File(Environment.getDataSystemDirectory(), HIBERNATION_DIR_NAME);
             return new HibernationStateDiskStore<>(
diff --git a/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java b/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java
index c83659d..24cf433 100644
--- a/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java
+++ b/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java
@@ -109,6 +109,7 @@
      * @return the parsed list of hibernation states, null if file does not exist
      */
     @Nullable
+    @WorkerThread
     List<T> readHibernationStates() {
         synchronized (this) {
             if (!mHibernationFile.exists()) {
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index ed3a223..6aec9fc 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -65,6 +65,7 @@
 
 import java.io.IOException;
 import java.io.RandomAccessFile;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 
@@ -80,27 +81,52 @@
     private static final String PIPE_NAME = "pipe:clipboard";
     private static final String PIPE_DEVICE = "/dev/qemu_pipe";
 
-    private void openPipe() {
+    private static byte[] createOpenHandshake() {
+        // String.getBytes doesn't include the null terminator,
+        // but the QEMU pipe device requires the pipe service name
+        // to be null-terminated.
+
+        final byte[] bits = Arrays.copyOf(PIPE_NAME.getBytes(), PIPE_NAME.length() + 1);
+        bits[PIPE_NAME.length()] = 0;
+        return bits;
+    }
+
+    private boolean openPipe() {
         try {
-            // String.getBytes doesn't include the null terminator,
-            // but the QEMU pipe device requires the pipe service name
-            // to be null-terminated.
-            byte[] b = new byte[PIPE_NAME.length() + 1];
-            b[PIPE_NAME.length()] = 0;
-            System.arraycopy(
-                PIPE_NAME.getBytes(),
-                0,
-                b,
-                0,
-                PIPE_NAME.length());
-            mPipe = new RandomAccessFile(PIPE_DEVICE, "rw");
-            mPipe.write(b);
-        } catch (IOException e) {
+            final RandomAccessFile pipe = new RandomAccessFile(PIPE_DEVICE, "rw");
             try {
-                if (mPipe != null) mPipe.close();
-            } catch (IOException ee) {}
-            mPipe = null;
+                pipe.write(createOpenHandshake());
+                mPipe = pipe;
+                return true;
+            } catch (IOException ignore) {
+                pipe.close();
+            }
+        } catch (IOException ignore) {
         }
+        return false;
+    }
+
+    private void closePipe() {
+        try {
+            final RandomAccessFile pipe = mPipe;
+            mPipe = null;
+            if (pipe != null) {
+                pipe.close();
+            }
+        } catch (IOException ignore) {
+        }
+    }
+
+    private byte[] receiveMessage() throws IOException {
+        final int size = Integer.reverseBytes(mPipe.readInt());
+        final byte[] receivedData = new byte[size];
+        mPipe.readFully(receivedData);
+        return receivedData;
+    }
+
+    private void sendMessage(byte[] message) throws IOException {
+        mPipe.writeInt(Integer.reverseBytes(message.length));
+        mPipe.write(message);
     }
 
     public HostClipboardMonitor(HostClipboardCallback cb) {
@@ -114,21 +140,15 @@
                 // There's no guarantee that QEMU pipes will be ready at the moment
                 // this method is invoked. We simply try to get the pipe open and
                 // retry on failure indefinitely.
-                while (mPipe == null) {
-                    openPipe();
+                while ((mPipe == null) && !openPipe()) {
                     Thread.sleep(100);
                 }
-                int size = mPipe.readInt();
-                size = Integer.reverseBytes(size);
-                byte[] receivedData = new byte[size];
-                mPipe.readFully(receivedData);
+
+                final byte[] receivedData = receiveMessage();
                 mHostClipboardCallback.onHostClipboardUpdated(
                     new String(receivedData));
             } catch (IOException e) {
-                try {
-                    mPipe.close();
-                } catch (IOException ee) {}
-                mPipe = null;
+                closePipe();
             } catch (InterruptedException e) {}
         }
     }
@@ -136,8 +156,7 @@
     public void setHostClipboard(String content) {
         try {
             if (mPipe != null) {
-                mPipe.writeInt(Integer.reverseBytes(content.getBytes().length));
-                mPipe.write(content.getBytes());
+                sendMessage(content.getBytes());
             }
         } catch(IOException e) {
             Slog.e("HostClipboardMonitor",
@@ -158,7 +177,7 @@
 
     private static final String TAG = "ClipboardService";
     private static final boolean IS_EMULATOR =
-        SystemProperties.getBoolean("ro.kernel.qemu", false);
+            SystemProperties.getBoolean("ro.boot.qemu", false);
 
     private final ActivityManagerInternal mAmInternal;
     private final IUriGrantsManager mUgm;
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index df83df9..d29a0c7 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -23,11 +23,14 @@
 import android.annotation.Nullable;
 import android.app.compat.PackageOverride;
 import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
 import android.compat.annotation.EnabledSince;
+import android.compat.annotation.Overridable;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 
+import com.android.internal.compat.AndroidBuildClassifier;
 import com.android.internal.compat.CompatibilityChangeInfo;
 import com.android.internal.compat.OverrideAllowedState;
 import com.android.server.compat.config.Change;
@@ -55,10 +58,19 @@
      * A change ID to be used only in the CTS test for this SystemApi
      */
     @ChangeId
-    @EnabledSince(targetSdkVersion = 1235) // Needs to be > test APK targetSdkVersion.
+    @EnabledSince(targetSdkVersion = 31) // Needs to be > test APK targetSdkVersion.
     static final long CTS_SYSTEM_API_CHANGEID = 149391281; // This is a bug id.
 
     /**
+     * An overridable change ID to be used only in the CTS test for this SystemApi
+     */
+    @ChangeId
+    @Disabled
+    @Overridable
+    static final long CTS_SYSTEM_API_OVERRIDABLE_CHANGEID = 174043039; // This is a bug id.
+
+
+    /**
      * Callback listener for when compat changes are updated for a package.
      * See {@link #registerListener(ChangeListener)} for more details.
      */
@@ -80,6 +92,15 @@
     }
 
     /**
+     * @param change an object generated by services/core/xsd/platform-compat-config.xsd
+     */
+    public CompatChange(Change change) {
+        this(change.getId(), change.getName(), change.getEnableAfterTargetSdk(),
+                change.getEnableSinceTargetSdk(), change.getDisabled(), change.getLoggingOnly(),
+                change.getDescription(), change.getOverridable());
+    }
+
+    /**
      * @param changeId Unique ID for the change. See {@link android.compat.Compatibility}.
      * @param name Short descriptive name.
      * @param enableAfterTargetSdk {@code targetSdkVersion} restriction. See {@link EnabledAfter};
@@ -93,15 +114,10 @@
             boolean overridable) {
         super(changeId, name, enableAfterTargetSdk, enableSinceTargetSdk, disabled, loggingOnly,
               description, overridable);
-    }
 
-    /**
-     * @param change an object generated by services/core/xsd/platform-compat-config.xsd
-     */
-    public CompatChange(Change change) {
-        super(change.getId(), change.getName(), change.getEnableAfterTargetSdk(),
-                change.getEnableSinceTargetSdk(), change.getDisabled(), change.getLoggingOnly(),
-                change.getDescription(), change.getOverridable());
+        // Initialize override maps.
+        mEvaluatedOverrides = new HashMap<>();
+        mRawOverrides = new HashMap<>();
     }
 
     void registerListener(ChangeListener listener) {
@@ -127,18 +143,13 @@
             throw new IllegalArgumentException(
                     "Can't add overrides for a logging only change " + toString());
         }
-        if (mEvaluatedOverrides == null) {
-            mEvaluatedOverrides = new HashMap<>();
-        }
         mEvaluatedOverrides.put(pname, enabled);
         notifyListener(pname);
     }
 
     private void removePackageOverrideInternal(String pname) {
-        if (mEvaluatedOverrides != null) {
-            if (mEvaluatedOverrides.remove(pname) != null) {
-                notifyListener(pname);
-            }
+        if (mEvaluatedOverrides.remove(pname) != null) {
+            notifyListener(pname);
         }
     }
 
@@ -157,9 +168,6 @@
             throw new IllegalArgumentException(
                     "Can't add overrides for a logging only change " + toString());
         }
-        if (mRawOverrides == null) {
-            mRawOverrides = new HashMap<>();
-        }
         mRawOverrides.put(packageName, override);
         recheckOverride(packageName, allowedState, context);
     }
@@ -212,8 +220,9 @@
     }
 
     boolean hasPackageOverride(String pname) {
-        return mRawOverrides != null && mRawOverrides.containsKey(pname);
+        return mRawOverrides.containsKey(pname);
     }
+
     /**
      * Remove any package override for the given package name, restoring the default behaviour.
      *
@@ -223,7 +232,7 @@
      */
     boolean removePackageOverride(String pname, OverrideAllowedState allowedState,
             Context context) {
-        if (mRawOverrides != null && (mRawOverrides.remove(pname) != null)) {
+        if (mRawOverrides.remove(pname) != null) {
             recheckOverride(pname, allowedState, context);
             return true;
         }
@@ -237,18 +246,24 @@
      * @param app Info about the app in question
      * @return {@code true} if the change should be enabled for the package.
      */
-    boolean isEnabled(ApplicationInfo app) {
+    boolean isEnabled(ApplicationInfo app, AndroidBuildClassifier buildClassifier) {
         if (app == null) {
             return defaultValue();
         }
-        if (mEvaluatedOverrides != null && mEvaluatedOverrides.containsKey(app.packageName)) {
+        if (mEvaluatedOverrides.containsKey(app.packageName)) {
             return mEvaluatedOverrides.get(app.packageName);
         }
         if (getDisabled()) {
             return false;
         }
         if (getEnableSinceTargetSdk() != -1) {
-            return app.targetSdkVersion >= getEnableSinceTargetSdk();
+            // If the change is gated by a platform version newer than the one currently installed
+            // on the device, disregard the app's target sdk version.
+            int compareSdk = Math.min(app.targetSdkVersion, buildClassifier.platformTargetSdk());
+            if (compareSdk != app.targetSdkVersion) {
+                compareSdk = app.targetSdkVersion;
+            }
+            return compareSdk >= getEnableSinceTargetSdk();
         }
         return true;
     }
@@ -289,7 +304,7 @@
      * @return true if there is such override
      */
     private boolean hasOverride(String packageName) {
-        return mEvaluatedOverrides != null && mEvaluatedOverrides.containsKey(packageName);
+        return mEvaluatedOverrides.containsKey(packageName);
     }
 
     /**
@@ -298,20 +313,15 @@
      * @return true if there is such a deferred override
      */
     private boolean hasRawOverride(String packageName) {
-        return mRawOverrides != null && mRawOverrides.containsKey(packageName);
+        return mRawOverrides.containsKey(packageName);
+    }
+
+    void clearOverrides() {
+        mRawOverrides.clear();
+        mEvaluatedOverrides.clear();
     }
 
     void loadOverrides(ChangeOverrides changeOverrides) {
-        if (mRawOverrides == null) {
-            mRawOverrides = new HashMap<>();
-        }
-        mRawOverrides.clear();
-
-        if (mEvaluatedOverrides == null) {
-            mEvaluatedOverrides = new HashMap<>();
-        }
-        mEvaluatedOverrides.clear();
-
         // Load deferred overrides for backwards compatibility
         if (changeOverrides.getDeferred() != null) {
             for (OverrideValue override : changeOverrides.getDeferred().getOverrideValue()) {
@@ -345,34 +355,30 @@
     }
 
     ChangeOverrides saveOverrides() {
-        if (mRawOverrides == null || mRawOverrides.isEmpty()) {
+        if (mRawOverrides.isEmpty()) {
             return null;
         }
         ChangeOverrides changeOverrides = new ChangeOverrides();
         changeOverrides.setChangeId(getId());
         ChangeOverrides.Raw rawOverrides = new ChangeOverrides.Raw();
         List<RawOverrideValue> rawList = rawOverrides.getRawOverrideValue();
-        if (mRawOverrides != null) {
-            for (Map.Entry<String, PackageOverride> entry : mRawOverrides.entrySet()) {
-                RawOverrideValue override = new RawOverrideValue();
-                override.setPackageName(entry.getKey());
-                override.setMinVersionCode(entry.getValue().getMinVersionCode());
-                override.setMaxVersionCode(entry.getValue().getMaxVersionCode());
-                override.setEnabled(entry.getValue().getEnabled());
-                rawList.add(override);
-            }
+        for (Map.Entry<String, PackageOverride> entry : mRawOverrides.entrySet()) {
+            RawOverrideValue override = new RawOverrideValue();
+            override.setPackageName(entry.getKey());
+            override.setMinVersionCode(entry.getValue().getMinVersionCode());
+            override.setMaxVersionCode(entry.getValue().getMaxVersionCode());
+            override.setEnabled(entry.getValue().isEnabled());
+            rawList.add(override);
         }
         changeOverrides.setRaw(rawOverrides);
 
         ChangeOverrides.Validated validatedOverrides = new ChangeOverrides.Validated();
         List<OverrideValue> validatedList = validatedOverrides.getOverrideValue();
-        if (mEvaluatedOverrides != null) {
-            for (Map.Entry<String, Boolean> entry : mEvaluatedOverrides.entrySet()) {
-                OverrideValue override = new OverrideValue();
-                override.setPackageName(entry.getKey());
-                override.setEnabled(entry.getValue());
-                validatedList.add(override);
-            }
+        for (Map.Entry<String, Boolean> entry : mEvaluatedOverrides.entrySet()) {
+            OverrideValue override = new OverrideValue();
+            override.setPackageName(entry.getKey());
+            override.setEnabled(entry.getValue());
+            validatedList.add(override);
         }
         changeOverrides.setValidated(validatedOverrides);
         return changeOverrides;
@@ -394,10 +400,10 @@
         if (getLoggingOnly()) {
             sb.append("; loggingOnly");
         }
-        if (mEvaluatedOverrides != null && mEvaluatedOverrides.size() > 0) {
+        if (!mEvaluatedOverrides.isEmpty()) {
             sb.append("; packageOverrides=").append(mEvaluatedOverrides);
         }
-        if (mRawOverrides != null && mRawOverrides.size() > 0) {
+        if (!mRawOverrides.isEmpty()) {
             sb.append("; rawOverrides=").append(mRawOverrides);
         }
         if (getOverridable()) {
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 66a6520..55e2696 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -67,18 +67,21 @@
 
     private static final String TAG = "CompatConfig";
     private static final String APP_COMPAT_DATA_DIR = "/data/misc/appcompat";
+    private static final String STATIC_OVERRIDES_PRODUCT_DIR = "/product/etc/appcompat";
     private static final String OVERRIDES_FILE = "compat_framework_overrides.xml";
 
     @GuardedBy("mChanges")
     private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
 
     private final OverrideValidatorImpl mOverrideValidator;
+    private final AndroidBuildClassifier mAndroidBuildClassifier;
     private Context mContext;
     private File mOverridesFile;
 
     @VisibleForTesting
     CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) {
         mOverrideValidator = new OverrideValidatorImpl(androidBuildClassifier, context, this);
+        mAndroidBuildClassifier = androidBuildClassifier;
         mContext = context;
     }
 
@@ -94,8 +97,7 @@
             config.initConfigFromLib(Environment.buildPath(
                     apex.apexDirectory, "etc", "compatconfig"));
         }
-        File overridesFile = new File(APP_COMPAT_DATA_DIR, OVERRIDES_FILE);
-        config.initOverrides(overridesFile);
+        config.initOverrides();
         config.invalidateCache();
         return config;
     }
@@ -133,7 +135,7 @@
         synchronized (mChanges) {
             for (int i = 0; i < mChanges.size(); ++i) {
                 CompatChange c = mChanges.valueAt(i);
-                if (!c.isEnabled(app)) {
+                if (!c.isEnabled(app, mAndroidBuildClassifier)) {
                     disabled.add(c.getId());
                 }
             }
@@ -175,7 +177,7 @@
                 // we know nothing about this change: default behaviour is enabled.
                 return true;
             }
-            return c.isEnabled(app);
+            return c.isEnabled(app, mAndroidBuildClassifier);
         }
     }
 
@@ -302,6 +304,16 @@
     }
 
     /**
+     * Returns whether the change is overridable.
+     */
+    boolean isOverridable(long changeId) {
+        synchronized (mChanges) {
+            CompatChange c = mChanges.get(changeId);
+            return c != null && c.getOverridable();
+        }
+    }
+
+    /**
      * Removes an override previously added via {@link #addOverride(long, String, boolean)}.
      *
      * <p>This restores the default behaviour for the given change and app, once any app processes
@@ -341,7 +353,7 @@
 
     /**
      * Removes all overrides previously added via {@link #addOverride(long, String, boolean)} or
-     * {@link #addOverrides(CompatibilityChangeConfig, String)} for a certain package.
+     * {@link #addOverrides(CompatibilityOverrideConfig, String)} for a certain package.
      *
      * <p>This restores the default behaviour for the given app.
      *
@@ -475,7 +487,7 @@
         synchronized (mChanges) {
             for (int i = 0; i < mChanges.size(); ++i) {
                 CompatChange c = mChanges.valueAt(i);
-                if (c.isEnabled(applicationInfo)) {
+                if (c.isEnabled(applicationInfo, mAndroidBuildClassifier)) {
                     enabled.add(c.getId());
                 } else {
                     disabled.add(c.getId());
@@ -525,10 +537,34 @@
         }
     }
 
-    void initOverrides(File overridesFile) {
+    private void initOverrides() {
+        initOverrides(new File(APP_COMPAT_DATA_DIR, OVERRIDES_FILE),
+                new File(STATIC_OVERRIDES_PRODUCT_DIR, OVERRIDES_FILE));
+    }
+
+    @VisibleForTesting
+    void initOverrides(File dynamicOverridesFile, File staticOverridesFile) {
+        // Clear overrides from all changes before loading.
+        synchronized (mChanges) {
+            for (int i = 0; i < mChanges.size(); ++i) {
+                mChanges.valueAt(i).clearOverrides();
+            }
+        }
+
+        loadOverrides(staticOverridesFile);
+
+        mOverridesFile = dynamicOverridesFile;
+        loadOverrides(dynamicOverridesFile);
+
+        if (staticOverridesFile.exists()) {
+            // Only save overrides if there is a static overrides file.
+            saveOverrides();
+        }
+    }
+
+    private void loadOverrides(File overridesFile) {
         if (!overridesFile.exists()) {
-            mOverridesFile = overridesFile;
-            // There have not been any overrides added yet.
+            // Overrides file doesn't exist.
             return;
         }
 
@@ -548,7 +584,6 @@
             Slog.w(TAG, "Error processing " + overridesFile + " " + e.toString());
             return;
         }
-        mOverridesFile = overridesFile;
     }
 
     /**
@@ -607,8 +642,11 @@
         }
         boolean shouldInvalidateCache = false;
         for (CompatChange c: changes) {
+            if (!c.hasPackageOverride(packageName)) {
+                continue;
+            }
             OverrideAllowedState allowedState =
-                    mOverrideValidator.getOverrideAllowedState(c.getId(), packageName);
+                    mOverrideValidator.getOverrideAllowedStateForRecheck(c.getId(), packageName);
             shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, mContext);
         }
         if (shouldInvalidateCache) {
diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
index fe5b4a9..b500691 100644
--- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
+++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
@@ -16,13 +16,18 @@
 
 package com.android.server.compat;
 
+import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
 import static com.android.internal.compat.OverrideAllowedState.ALLOWED;
 import static com.android.internal.compat.OverrideAllowedState.DEFERRED_VERIFICATION;
 import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK;
 import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE;
 import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH;
 import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE;
+import static com.android.internal.compat.OverrideAllowedState.PLATFORM_TOO_OLD;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -68,8 +73,25 @@
         mForceNonDebuggableFinalBuild = false;
     }
 
+    /**
+     * Check the allowed state for the given changeId and packageName on a recheck.
+     *
+     * <p>Recheck happens when the given app is getting updated. In this case we cannot do a
+     * permission check on the caller, so we're using the fact that the override was present as
+     * proof that the original caller was allowed to set this override.
+     */
+    OverrideAllowedState getOverrideAllowedStateForRecheck(long changeId,
+            @NonNull String packageName) {
+        return getOverrideAllowedStateInternal(changeId, packageName, true);
+    }
+
     @Override
     public OverrideAllowedState getOverrideAllowedState(long changeId, String packageName) {
+        return getOverrideAllowedStateInternal(changeId, packageName, false);
+    }
+
+    private OverrideAllowedState getOverrideAllowedStateInternal(long changeId, String packageName,
+            boolean isRecheck) {
         if (mCompatConfig.isLoggingOnly(changeId)) {
             return new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1);
         }
@@ -85,6 +107,9 @@
         if (debuggableBuild) {
             return new OverrideAllowedState(ALLOWED, -1, -1);
         }
+        if (maxTargetSdk >= mAndroidBuildClassifier.platformTargetSdk()) {
+            return new OverrideAllowedState(PLATFORM_TOO_OLD, -1, maxTargetSdk);
+        }
         PackageManager packageManager = mContext.getPackageManager();
         if (packageManager == null) {
             throw new IllegalStateException("No PackageManager!");
@@ -95,6 +120,16 @@
         } catch (NameNotFoundException e) {
             return new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1);
         }
+        // If the change is annotated as @Overridable, apps with the specific permission can
+        // set the override even on production builds. When rechecking the override, e.g. during an
+        // app update we can bypass this check, as it wouldn't have been here in the first place.
+        if (mCompatConfig.isOverridable(changeId)
+                && (isRecheck
+                        || mContext.checkCallingOrSelfPermission(
+                                OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
+                                        == PERMISSION_GRANTED)) {
+            return new OverrideAllowedState(ALLOWED, -1, -1);
+        }
         int appTargetSdk = applicationInfo.targetSdkVersion;
         // Only allow overriding debuggable apps.
         if ((applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
@@ -126,5 +161,4 @@
     void forceNonDebuggableFinalForTest(boolean value) {
         mForceNonDebuggableFinalBuild = value;
     }
-
 }
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index edfc8b8..20469a2 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.LOG_COMPAT_CHANGE;
 import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG;
+import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD;
 import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Process.SYSTEM_UID;
@@ -66,18 +67,22 @@
     private final Context mContext;
     private final ChangeReporter mChangeReporter;
     private final CompatConfig mCompatConfig;
+    private final AndroidBuildClassifier mBuildClassifier;
 
     public PlatformCompat(Context context) {
         mContext = context;
         mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
-        mCompatConfig = CompatConfig.create(new AndroidBuildClassifier(), mContext);
+        mBuildClassifier = new AndroidBuildClassifier();
+        mCompatConfig = CompatConfig.create(mBuildClassifier, mContext);
     }
 
     @VisibleForTesting
-    PlatformCompat(Context context, CompatConfig compatConfig) {
+    PlatformCompat(Context context, CompatConfig compatConfig,
+                   AndroidBuildClassifier buildClassifier) {
         mContext = context;
         mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
         mCompatConfig = compatConfig;
+        mBuildClassifier = buildClassifier;
 
         registerPackageReceiver(context);
     }
@@ -178,11 +183,12 @@
     }
 
     @Override
-    public void setOverridesFromInstaller(CompatibilityOverrideConfig overrides,
+    public void setOverridesOnReleaseBuilds(CompatibilityOverrideConfig overrides,
             String packageName) {
-        checkCompatChangeOverridePermission();
+        // TODO(b/183630314): Unify the permission enforcement with the other setOverrides* methods.
+        checkCompatChangeOverrideOverridablePermission();
+        checkAllCompatOverridesAreOverridable(overrides);
         mCompatConfig.addOverrides(overrides, packageName);
-        killPackage(packageName);
     }
 
     @Override
@@ -379,6 +385,26 @@
         }
     }
 
+    private void checkCompatChangeOverrideOverridablePermission() {
+        // Don't check for permissions within the system process
+        if (Binder.getCallingUid() == SYSTEM_UID) {
+            return;
+        }
+        if (mContext.checkCallingOrSelfPermission(OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
+                != PERMISSION_GRANTED) {
+            throw new SecurityException("Cannot override compat change");
+        }
+    }
+
+    private void checkAllCompatOverridesAreOverridable(CompatibilityOverrideConfig overrides) {
+        for (Long changeId : overrides.overrides.keySet()) {
+            if (!mCompatConfig.isOverridable(changeId)) {
+                throw new SecurityException("Only change ids marked as Overridable can be "
+                        + "overridden.");
+            }
+        }
+    }
+
     private void checkCompatChangeReadAndLogPermission() {
         checkCompatChangeReadPermission();
         checkCompatChangeLogPermission();
@@ -392,7 +418,8 @@
             return false;
         }
         if (change.getEnableSinceTargetSdk() > 0) {
-            return change.getEnableSinceTargetSdk() >= Build.VERSION_CODES.Q;
+            return change.getEnableSinceTargetSdk() >= Build.VERSION_CODES.Q
+                && change.getEnableSinceTargetSdk() <= mBuildClassifier.platformTargetSdk();
         }
         return true;
     }
diff --git a/services/core/java/com/android/server/connectivity/ConnectivityConstants.java b/services/core/java/com/android/server/connectivity/ConnectivityConstants.java
index 0fb6fec..325a2cd 100644
--- a/services/core/java/com/android/server/connectivity/ConnectivityConstants.java
+++ b/services/core/java/com/android/server/connectivity/ConnectivityConstants.java
@@ -18,18 +18,10 @@
 
 /**
  * A class encapsulating various constants used by Connectivity.
+ * TODO : remove this class.
  * @hide
  */
 public class ConnectivityConstants {
-
-    // Penalty applied to scores of Networks that have not been validated.
-    public static final int UNVALIDATED_SCORE_PENALTY = 40;
-
-    // Score for explicitly connected network.
-    //
-    // This ensures that a) the explicitly selected network is never trumped by anything else, and
-    // b) the explicitly selected network is never torn down.
-    public static final int EXPLICITLY_SELECTED_NETWORK_SCORE = 100;
     // VPNs typically have priority over other networks. Give them a score that will
     // let them win every single time.
     public static final int VPN_DEFAULT_SCORE = 101;
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index 4f6b530..ffeb77d 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -16,23 +16,23 @@
 
 package com.android.server.connectivity;
 
-import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
 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_SPECIFIER;
 import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_FAILURE;
 import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_SUCCESS;
-import static android.provider.Settings.Global.DNS_RESOLVER_MAX_SAMPLES;
-import static android.provider.Settings.Global.DNS_RESOLVER_MIN_SAMPLES;
-import static android.provider.Settings.Global.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS;
-import static android.provider.Settings.Global.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT;
-import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE;
-import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
-import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
 
 import android.annotation.NonNull;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.net.ConnectivityManager;
 import android.net.IDnsResolver;
 import android.net.InetAddresses;
 import android.net.LinkProperties;
@@ -127,13 +127,17 @@
     private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
     private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
 
-    public static PrivateDnsConfig getPrivateDnsConfig(ContentResolver cr) {
-        final String mode = getPrivateDnsMode(cr);
+    /**
+     * Get PrivateDnsConfig.
+     */
+    public static PrivateDnsConfig getPrivateDnsConfig(Context context) {
+        final String mode = ConnectivityManager.getPrivateDnsMode(context);
 
         final boolean useTls = !TextUtils.isEmpty(mode) && !PRIVATE_DNS_MODE_OFF.equals(mode);
 
         if (PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals(mode)) {
-            final String specifier = getStringSetting(cr, PRIVATE_DNS_SPECIFIER);
+            final String specifier = getStringSetting(context.getContentResolver(),
+                    PRIVATE_DNS_SPECIFIER);
             return new PrivateDnsConfig(specifier, null);
         }
 
@@ -268,7 +272,7 @@
     }
 
     public PrivateDnsConfig getPrivateDnsConfig() {
-        return getPrivateDnsConfig(mContentResolver);
+        return getPrivateDnsConfig(mContext);
     }
 
     public void removeNetwork(Network network) {
@@ -479,13 +483,6 @@
         return result;
     }
 
-    private static String getPrivateDnsMode(ContentResolver cr) {
-        String mode = getStringSetting(cr, PRIVATE_DNS_MODE);
-        if (TextUtils.isEmpty(mode)) mode = getStringSetting(cr, PRIVATE_DNS_DEFAULT_MODE);
-        if (TextUtils.isEmpty(mode)) mode = PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
-        return mode;
-    }
-
     private static String getStringSetting(ContentResolver cr, String which) {
         return Settings.Global.getString(cr, which);
     }
diff --git a/services/core/java/com/android/server/connectivity/FullScore.java b/services/core/java/com/android/server/connectivity/FullScore.java
new file mode 100644
index 0000000..028cfee
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/FullScore.java
@@ -0,0 +1,211 @@
+/*
+ * 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 static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.net.NetworkAgentConfig;
+import android.net.NetworkCapabilities;
+import android.net.NetworkScore;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.StringJoiner;
+
+/**
+ * This class represents how desirable a network is.
+ *
+ * FullScore is very similar to NetworkScore, but it contains the bits that are managed
+ * by ConnectivityService. This provides static guarantee that all users must know whether
+ * they are handling a score that had the CS-managed bits set.
+ */
+public class FullScore {
+    // This will be removed soon. Do *NOT* depend on it for any new code that is not part of
+    // a migration.
+    private final int mLegacyInt;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"POLICY_"}, value = {
+            POLICY_IS_VALIDATED,
+            POLICY_IS_VPN,
+            POLICY_EVER_USER_SELECTED,
+            POLICY_ACCEPT_UNVALIDATED
+    })
+    public @interface Policy {
+    }
+
+    // Agent-managed policies are in NetworkScore. They start from 1.
+    // CS-managed policies, counting from 63 downward
+    // This network is validated. CS-managed because the source of truth is in NetworkCapabilities.
+    /** @hide */
+    public static final int POLICY_IS_VALIDATED = 63;
+
+    // This is a VPN and behaves as one for scoring purposes.
+    /** @hide */
+    public static final int POLICY_IS_VPN = 62;
+
+    // This network has been selected by the user manually from settings or a 3rd party app
+    // at least once. {@see NetworkAgentConfig#explicitlySelected}.
+    /** @hide */
+    public static final int POLICY_EVER_USER_SELECTED = 61;
+
+    // The user has indicated in UI that this network should be used even if it doesn't
+    // validate. {@see NetworkAgentConfig#acceptUnvalidated}.
+    /** @hide */
+    public static final int POLICY_ACCEPT_UNVALIDATED = 60;
+
+    // To help iterate when printing
+    @VisibleForTesting
+    static final int MIN_CS_MANAGED_POLICY = POLICY_ACCEPT_UNVALIDATED;
+    @VisibleForTesting
+    static final int MAX_CS_MANAGED_POLICY = POLICY_IS_VALIDATED;
+
+    @VisibleForTesting
+    static @NonNull String policyNameOf(final int policy) {
+        switch (policy) {
+            case POLICY_IS_VALIDATED: return "IS_VALIDATED";
+            case POLICY_IS_VPN: return "IS_VPN";
+            case POLICY_EVER_USER_SELECTED: return "EVER_USER_SELECTED";
+            case POLICY_ACCEPT_UNVALIDATED: return "ACCEPT_UNVALIDATED";
+        }
+        throw new IllegalArgumentException("Unknown policy : " + policy);
+    }
+
+    // Bitmask of all the policies applied to this score.
+    private final long mPolicies;
+
+    FullScore(final int legacyInt, final long policies) {
+        mLegacyInt = legacyInt;
+        mPolicies = policies;
+    }
+
+    /**
+     * Given a score supplied by the NetworkAgent and CS-managed objects, produce a full score.
+     *
+     * @param score the score supplied by the agent
+     * @param caps the NetworkCapabilities of the network
+     * @param config the NetworkAgentConfig of the network
+     * @return an FullScore that is appropriate to use for ranking.
+     */
+    public static FullScore fromNetworkScore(@NonNull final NetworkScore score,
+            @NonNull final NetworkCapabilities caps, @NonNull final NetworkAgentConfig config) {
+        return withPolicies(score.getLegacyInt(), caps.hasCapability(NET_CAPABILITY_VALIDATED),
+                caps.hasTransport(TRANSPORT_VPN),
+                config.explicitlySelected,
+                config.acceptUnvalidated);
+    }
+
+    /**
+     * Return a new score given updated caps and config.
+     *
+     * @param caps the NetworkCapabilities of the network
+     * @param config the NetworkAgentConfig of the network
+     * @return a score with the policies from the arguments reset
+     */
+    public FullScore mixInScore(@NonNull final NetworkCapabilities caps,
+            @NonNull final NetworkAgentConfig config) {
+        return withPolicies(mLegacyInt, caps.hasCapability(NET_CAPABILITY_VALIDATED),
+                caps.hasTransport(TRANSPORT_VPN),
+                config.explicitlySelected,
+                config.acceptUnvalidated);
+    }
+
+    private static FullScore withPolicies(@NonNull final int legacyInt,
+            final boolean isValidated,
+            final boolean isVpn,
+            final boolean everUserSelected,
+            final boolean acceptUnvalidated) {
+        return new FullScore(legacyInt,
+                (isValidated         ? 1L << POLICY_IS_VALIDATED : 0)
+                | (isVpn             ? 1L << POLICY_IS_VPN : 0)
+                | (everUserSelected  ? 1L << POLICY_EVER_USER_SELECTED : 0)
+                | (acceptUnvalidated ? 1L << POLICY_ACCEPT_UNVALIDATED : 0));
+    }
+
+    /**
+     * For backward compatibility, get the legacy int.
+     * This will be removed before S is published.
+     */
+    public int getLegacyInt() {
+        return getLegacyInt(false /* pretendValidated */);
+    }
+
+    public int getLegacyIntAsValidated() {
+        return getLegacyInt(true /* pretendValidated */);
+    }
+
+    // TODO : remove these two constants
+    // Penalty applied to scores of Networks that have not been validated.
+    private static final int UNVALIDATED_SCORE_PENALTY = 40;
+
+    // Score for a network that can be used unvalidated
+    private static final int ACCEPT_UNVALIDATED_NETWORK_SCORE = 100;
+
+    private int getLegacyInt(boolean pretendValidated) {
+        // If the user has chosen this network at least once, give it the maximum score when
+        // checking to pretend it's validated, or if it doesn't need to validate because the
+        // user said to use it even if it doesn't validate.
+        // This ensures that networks that have been selected in UI are not torn down before the
+        // user gets a chance to prefer it when a higher-scoring network (e.g., Ethernet) is
+        // available.
+        if (hasPolicy(POLICY_EVER_USER_SELECTED)
+                && (hasPolicy(POLICY_ACCEPT_UNVALIDATED) || pretendValidated)) {
+            return ACCEPT_UNVALIDATED_NETWORK_SCORE;
+        }
+
+        int score = mLegacyInt;
+        // Except for VPNs, networks are subject to a penalty for not being validated.
+        // Apply the penalty unless the network is a VPN, or it's validated or pretending to be.
+        if (!hasPolicy(POLICY_IS_VALIDATED) && !pretendValidated && !hasPolicy(POLICY_IS_VPN)) {
+            score -= UNVALIDATED_SCORE_PENALTY;
+        }
+        if (score < 0) score = 0;
+        return score;
+    }
+
+    /**
+     * @return whether this score has a particular policy.
+     */
+    @VisibleForTesting
+    public boolean hasPolicy(final int policy) {
+        return 0 != (mPolicies & (1L << policy));
+    }
+
+    // Example output :
+    // Score(50 ; Policies : EVER_USER_SELECTED&IS_VALIDATED)
+    @Override
+    public String toString() {
+        final StringJoiner sj = new StringJoiner(
+                "&", // delimiter
+                "Score(" + mLegacyInt + " ; Policies : ", // prefix
+                ")"); // suffix
+        for (int i = NetworkScore.MIN_AGENT_MANAGED_POLICY;
+                i <= NetworkScore.MAX_AGENT_MANAGED_POLICY; ++i) {
+            if (hasPolicy(i)) sj.add(policyNameOf(i));
+        }
+        for (int i = MIN_CS_MANAGED_POLICY; i <= MAX_CS_MANAGED_POLICY; ++i) {
+            if (hasPolicy(i)) sj.add(policyNameOf(i));
+        }
+        return sj.toString();
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/MockableSystemProperties.java b/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
index ef76734..a25b89a 100644
--- a/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
+++ b/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
@@ -17,7 +17,6 @@
 package com.android.server.connectivity;
 
 import android.os.SystemProperties;
-import android.sysprop.NetworkProperties;
 
 public class MockableSystemProperties {
 
@@ -32,10 +31,4 @@
     public boolean getBoolean(String key, boolean def) {
         return SystemProperties.getBoolean(key, def);
     }
-    /**
-     * Set net.tcp_def_init_rwnd to the tcp initial receive window size.
-     */
-    public void setTcpInitRwnd(int value) {
-        NetworkProperties.tcp_init_rwnd(value);
-    }
 }
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index fa80b25..c66a280 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -16,6 +16,8 @@
 
 package com.android.server.connectivity;
 
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+
 import static com.android.net.module.util.CollectionUtils.contains;
 
 import android.annotation.NonNull;
@@ -35,6 +37,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.net.module.util.NetworkStackConstants;
+import com.android.server.ConnectivityService;
 
 import java.net.Inet6Address;
 import java.util.Objects;
@@ -94,12 +97,15 @@
     private Inet6Address mIPv6Address;
     private State mState = State.IDLE;
 
+    private boolean mEnableClatOnCellular;
     private boolean mPrefixDiscoveryRunning;
 
-    public Nat464Xlat(NetworkAgentInfo nai, INetd netd, IDnsResolver dnsResolver) {
+    public Nat464Xlat(NetworkAgentInfo nai, INetd netd, IDnsResolver dnsResolver,
+            ConnectivityService.Dependencies deps) {
         mDnsResolver = dnsResolver;
         mNetd = netd;
         mNetwork = nai;
+        mEnableClatOnCellular = deps.getCellular464XlatEnabled();
     }
 
     /**
@@ -111,7 +117,7 @@
      * @return true if the network requires clat, false otherwise.
      */
     @VisibleForTesting
-    protected static boolean requiresClat(NetworkAgentInfo nai) {
+    protected boolean requiresClat(NetworkAgentInfo nai) {
         // TODO: migrate to NetworkCapabilities.TRANSPORT_*.
         final boolean supported = contains(NETWORK_TYPES, nai.networkInfo.getType());
         final boolean connected = contains(NETWORK_STATES, nai.networkInfo.getState());
@@ -126,7 +132,9 @@
         final boolean skip464xlat = (nai.netAgentConfig() != null)
                 && nai.netAgentConfig().skip464xlat;
 
-        return supported && connected && isIpv6OnlyNetwork && !skip464xlat;
+        return supported && connected && isIpv6OnlyNetwork && !skip464xlat
+            && (nai.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)
+                ? isCellular464XlatEnabled() : true);
     }
 
     /**
@@ -137,7 +145,7 @@
      * @return true if the network should start clat, false otherwise.
      */
     @VisibleForTesting
-    protected static boolean shouldStartClat(NetworkAgentInfo nai) {
+    protected boolean shouldStartClat(NetworkAgentInfo nai) {
         LinkProperties lp = nai.linkProperties;
         return requiresClat(nai) && lp != null && lp.getNat64Prefix() != null;
     }
@@ -507,4 +515,9 @@
     protected int getNetId() {
         return mNetwork.network.getNetId();
     }
+
+    @VisibleForTesting
+    protected boolean isCellular464XlatEnabled() {
+        return mEnableClatOnCellular;
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 1d0e115..97df5bf 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -25,6 +25,8 @@
 import android.net.CaptivePortalData;
 import android.net.IDnsResolver;
 import android.net.INetd;
+import android.net.INetworkAgent;
+import android.net.INetworkAgentRegistry;
 import android.net.INetworkMonitor;
 import android.net.LinkProperties;
 import android.net.NattKeepalivePacketData;
@@ -35,6 +37,7 @@
 import android.net.NetworkInfo;
 import android.net.NetworkMonitorManager;
 import android.net.NetworkRequest;
+import android.net.NetworkScore;
 import android.net.NetworkStateSnapshot;
 import android.net.QosCallbackException;
 import android.net.QosFilter;
@@ -46,12 +49,11 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.telephony.data.NrQosSessionAttributes;
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
 
-import com.android.connectivity.aidl.INetworkAgent;
-import com.android.connectivity.aidl.INetworkAgentRegistry;
 import com.android.internal.util.WakeupMessage;
 import com.android.server.ConnectivityService;
 
@@ -302,8 +304,9 @@
     // validated).
     private boolean mInactive;
 
-    // This represents the quality of the network with no clear scale.
-    private int mScore;
+    // This represents the quality of the network. As opposed to NetworkScore, FullScore includes
+    // the ConnectivityService-managed bits.
+    private FullScore mScore;
 
     // The list of NetworkRequests being satisfied by this Network.
     private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>();
@@ -338,10 +341,11 @@
     private final QosCallbackTracker mQosCallbackTracker;
 
     public NetworkAgentInfo(INetworkAgent na, Network net, NetworkInfo info,
-            @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc, int score, Context context,
+            @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc,
+            @NonNull NetworkScore score, Context context,
             Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
             IDnsResolver dnsResolver, int factorySerialNumber, int creatorUid,
-            QosCallbackTracker qosCallbackTracker) {
+            QosCallbackTracker qosCallbackTracker, ConnectivityService.Dependencies deps) {
         Objects.requireNonNull(net);
         Objects.requireNonNull(info);
         Objects.requireNonNull(lp);
@@ -354,12 +358,12 @@
         networkInfo = info;
         linkProperties = lp;
         networkCapabilities = nc;
-        mScore = score;
-        clatd = new Nat464Xlat(this, netd, dnsResolver);
+        networkAgentConfig = config;
+        setScore(score); // uses members networkCapabilities and networkAgentConfig
+        clatd = new Nat464Xlat(this, netd, dnsResolver, deps);
         mConnService = connService;
         mContext = context;
         mHandler = handler;
-        networkAgentConfig = config;
         this.factorySerialNumber = factorySerialNumber;
         this.creatorUid = creatorUid;
         mQosCallbackTracker = qosCallbackTracker;
@@ -573,6 +577,28 @@
         }
     }
 
+    /**
+     * Notify the NetworkAgent that the network is successfully connected.
+     */
+    public void onNetworkCreated() {
+        try {
+            networkAgent.onNetworkCreated();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error sending network created event", e);
+        }
+    }
+
+    /**
+     * Notify the NetworkAgent that the network is disconnected and destroyed.
+     */
+    public void onNetworkDisconnected() {
+        try {
+            networkAgent.onNetworkDisconnected();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error sending network disconnected event", e);
+        }
+    }
+
     // TODO: consider moving out of NetworkAgentInfo into its own class
     private class NetworkAgentMessageHandler extends INetworkAgentRegistry.Stub {
         private final Handler mHandler;
@@ -603,9 +629,9 @@
         }
 
         @Override
-        public void sendScore(int score) {
-            mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_SCORE_CHANGED, score, 0,
-                    new Pair<>(NetworkAgentInfo.this, null)).sendToTarget();
+        public void sendScore(@NonNull final NetworkScore score) {
+            mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_SCORE_CHANGED,
+                    new Pair<>(NetworkAgentInfo.this, score)).sendToTarget();
         }
 
         @Override
@@ -630,7 +656,13 @@
         @Override
         public void sendEpsQosSessionAvailable(final int qosCallbackId, final QosSession session,
                 final EpsBearerQosSessionAttributes attributes) {
-            mQosCallbackTracker.sendEventQosSessionAvailable(qosCallbackId, session, attributes);
+            mQosCallbackTracker.sendEventEpsQosSessionAvailable(qosCallbackId, session, attributes);
+        }
+
+        @Override
+        public void sendNrQosSessionAvailable(final int qosCallbackId, final QosSession session,
+                final NrQosSessionAttributes attributes) {
+            mQosCallbackTracker.sendEventNrQosSessionAvailable(qosCallbackId, session, attributes);
         }
 
         @Override
@@ -665,6 +697,7 @@
             @NonNull final NetworkCapabilities nc) {
         final NetworkCapabilities oldNc = networkCapabilities;
         networkCapabilities = nc;
+        mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig);
         final NetworkMonitorManager nm = mNetworkMonitor;
         if (nm != null) {
             nm.notifyNetworkCapabilitiesChanged(nc);
@@ -717,6 +750,7 @@
                 break;
 
             case LISTEN:
+            case LISTEN_FOR_BEST:
             case TRACK_DEFAULT:
             case TRACK_SYSTEM_DEFAULT:
                 break;
@@ -841,30 +875,6 @@
         return isVPN();
     }
 
-    private int getCurrentScore(boolean pretendValidated) {
-        // TODO: We may want to refactor this into a NetworkScore class that takes a base score from
-        // the NetworkAgent and signals from the NetworkAgent and uses those signals to modify the
-        // score.  The NetworkScore class would provide a nice place to centralize score constants
-        // so they are not scattered about the transports.
-
-        // If this network is explicitly selected and the user has decided to use it even if it's
-        // unvalidated, give it the maximum score. Also give it the maximum score if it's explicitly
-        // selected and we're trying to see what its score could be. This ensures that we don't tear
-        // down an explicitly selected network before the user gets a chance to prefer it when
-        // a higher-scoring network (e.g., Ethernet) is available.
-        if (networkAgentConfig.explicitlySelected
-                && (networkAgentConfig.acceptUnvalidated || pretendValidated)) {
-            return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE;
-        }
-
-        int score = mScore;
-        if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) {
-            score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY;
-        }
-        if (score < 0) score = 0;
-        return score;
-    }
-
     // Return true on devices configured to ignore score penalty for wifi networks
     // that become unvalidated (b/31075769).
     private boolean ignoreWifiUnvalidationPenalty() {
@@ -877,17 +887,29 @@
     // Get the current score for this Network.  This may be modified from what the
     // NetworkAgent sent, as it has modifiers applied to it.
     public int getCurrentScore() {
-        return getCurrentScore(false);
+        return mScore.getLegacyInt();
     }
 
     // Get the current score for this Network as if it was validated.  This may be modified from
     // what the NetworkAgent sent, as it has modifiers applied to it.
     public int getCurrentScoreAsValidated() {
-        return getCurrentScore(true);
+        return mScore.getLegacyIntAsValidated();
     }
 
-    public void setScore(final int score) {
-        mScore = score;
+    /**
+     * Mix-in the ConnectivityService-managed bits in the score.
+     */
+    public void setScore(final NetworkScore score) {
+        mScore = FullScore.fromNetworkScore(score, networkCapabilities, networkAgentConfig);
+    }
+
+    /**
+     * Update the ConnectivityService-managed bits in the score.
+     *
+     * Call this after updating the network agent config.
+     */
+    public void updateScoreForNetworkAgentConfigUpdate() {
+        mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig);
     }
 
     /**
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 508739f..0c0d459 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -28,6 +28,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
+import android.graphics.drawable.Icon;
+import android.net.ConnectivityResources;
 import android.net.NetworkSpecifier;
 import android.net.TelephonyNetworkSpecifier;
 import android.net.wifi.WifiInfo;
@@ -40,7 +42,7 @@
 import android.util.SparseIntArray;
 import android.widget.Toast;
 
-import com.android.internal.R;
+import com.android.connectivity.resources.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 
@@ -82,6 +84,7 @@
 
     // The context is for the current user (system server)
     private final Context mContext;
+    private final Resources mResources;
     private final TelephonyManager mTelephonyManager;
     // The notification manager is created from a context for User.ALL, so notifications
     // will be sent to all users.
@@ -96,6 +99,7 @@
                 (NotificationManager) c.createContextAsUser(UserHandle.ALL, 0 /* flags */)
                         .getSystemService(Context.NOTIFICATION_SERVICE);
         mNotificationTypeMap = new SparseIntArray();
+        mResources = new ConnectivityResources(mContext).get();
     }
 
     @VisibleForTesting
@@ -113,20 +117,19 @@
         return -1;
     }
 
-    private static String getTransportName(final int transportType) {
-        Resources r = Resources.getSystem();
-        String[] networkTypes = r.getStringArray(R.array.network_switch_type_name);
+    private String getTransportName(final int transportType) {
+        String[] networkTypes = mResources.getStringArray(R.array.network_switch_type_name);
         try {
             return networkTypes[transportType];
         } catch (IndexOutOfBoundsException e) {
-            return r.getString(R.string.network_switch_type_name_unknown);
+            return mResources.getString(R.string.network_switch_type_name_unknown);
         }
     }
 
     private static int getIcon(int transportType) {
         return (transportType == TRANSPORT_WIFI)
-                ? R.drawable.stat_notify_wifi_in_range :  // TODO: Distinguish ! from ?.
-                R.drawable.stat_notify_rssi_in_range;
+                ? R.drawable.stat_notify_wifi_in_range  // TODO: Distinguish ! from ?.
+                : R.drawable.stat_notify_rssi_in_range;
     }
 
     /**
@@ -156,7 +159,7 @@
         final String tag = tagFor(id);
         final int eventId = notifyType.eventId;
         final int transportType;
-        final String name;
+        final CharSequence name;
         if (nai != null) {
             transportType = approximateTransportType(nai);
             final String extraInfo = nai.networkInfo.getExtraInfo();
@@ -194,10 +197,10 @@
                     tag, nameOf(eventId), getTransportName(transportType), name, highPriority));
         }
 
-        Resources r = mContext.getResources();
+        final Resources r = mResources;
         final CharSequence title;
         final CharSequence details;
-        int icon = getIcon(transportType);
+        Icon icon = Icon.createWithResource(r, getIcon(transportType));
         if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
             title = r.getString(R.string.wifi_no_internet, name);
             details = r.getString(R.string.wifi_no_internet_detailed);
@@ -272,8 +275,7 @@
                 .setSmallIcon(icon)
                 .setAutoCancel(true)
                 .setTicker(title)
-                .setColor(mContext.getColor(
-                        com.android.internal.R.color.system_notification_accent_color))
+                .setColor(mContext.getColor(android.R.color.system_notification_accent_color))
                 .setContentTitle(title)
                 .setContentIntent(intent)
                 .setLocalOnly(true)
@@ -353,7 +355,7 @@
     public void showToast(NetworkAgentInfo fromNai, NetworkAgentInfo toNai) {
         String fromTransport = getTransportName(approximateTransportType(fromNai));
         String toTransport = getTransportName(approximateTransportType(toNai));
-        String text = mContext.getResources().getString(
+        String text = mResources.getString(
                 R.string.network_switch_metered_toast, fromTransport, toTransport);
         Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
     }
diff --git a/services/core/java/com/android/server/connectivity/PacProxyInstaller.java b/services/core/java/com/android/server/connectivity/PacProxyService.java
similarity index 75%
rename from services/core/java/com/android/server/connectivity/PacProxyInstaller.java
rename to services/core/java/com/android/server/connectivity/PacProxyService.java
index aadaf4d..d23b488 100644
--- a/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
+++ b/services/core/java/com/android/server/connectivity/PacProxyService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.connectivity;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.WorkerThread;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -26,12 +28,16 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.net.IPacProxyInstalledListener;
+import android.net.IPacProxyManager;
 import android.net.ProxyInfo;
 import android.net.TrafficStats;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -44,6 +50,7 @@
 import com.android.net.IProxyCallback;
 import com.android.net.IProxyPortListener;
 import com.android.net.IProxyService;
+import com.android.net.module.util.PermissionUtils;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -53,7 +60,7 @@
 /**
  * @hide
  */
-public class PacProxyInstaller {
+public class PacProxyService extends IPacProxyManager.Stub {
     private static final String PAC_PACKAGE = "com.android.pacprocessor";
     private static final String PAC_SERVICE = "com.android.pacprocessor.PacService";
     private static final String PAC_SERVICE_NAME = "com.android.net.IProxyService";
@@ -61,7 +68,7 @@
     private static final String PROXY_PACKAGE = "com.android.proxyhandler";
     private static final String PROXY_SERVICE = "com.android.proxyhandler.ProxyService";
 
-    private static final String TAG = "PacProxyInstaller";
+    private static final String TAG = "PacProxyService";
 
     private static final String ACTION_PAC_REFRESH = "android.net.proxy.PAC_REFRESH";
 
@@ -71,10 +78,6 @@
     private static final int DELAY_LONG = 4;
     private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
 
-    // Return values for #setCurrentProxyScriptUrl
-    public static final boolean DONT_SEND_BROADCAST = false;
-    public static final boolean DO_SEND_BROADCAST = true;
-
     private String mCurrentPac;
     @GuardedBy("mProxyLock")
     private volatile Uri mPacUrl = Uri.EMPTY;
@@ -93,8 +96,8 @@
     private volatile boolean mHasSentBroadcast;
     private volatile boolean mHasDownloaded;
 
-    private Handler mConnectivityHandler;
-    private final int mProxyMessage;
+    private final RemoteCallbackList<IPacProxyInstalledListener>
+            mCallbacks = new RemoteCallbackList<>();
 
     /**
      * Used for locking when setting mProxyService and all references to mCurrentPac.
@@ -102,6 +105,13 @@
     private final Object mProxyLock = new Object();
 
     /**
+     * Lock ensuring consistency between the values of mHasSentBroadcast, mHasDownloaded, the
+     * last URL and port, and the broadcast message being sent with the correct arguments.
+     * TODO : this should probably protect all instances of these variables
+     */
+    private final Object mBroadcastStateLock = new Object();
+
+    /**
      * Runnable to download PAC script.
      * The behavior relies on the assumption it always runs on mNetThread to guarantee that the
      * latest data fetched from mPacUrl is stored in mProxyService.
@@ -146,10 +156,10 @@
         }
     }
 
-    public PacProxyInstaller(Context context, Handler handler, int proxyMessage) {
+    public PacProxyService(@NonNull Context context) {
         mContext = context;
         mLastPort = -1;
-        final HandlerThread netThread = new HandlerThread("android.pacproxyinstaller",
+        final HandlerThread netThread = new HandlerThread("android.pacproxyservice",
                 android.os.Process.THREAD_PRIORITY_DEFAULT);
         netThread.start();
         mNetThreadHandler = new Handler(netThread.getLooper());
@@ -158,8 +168,6 @@
                 context, 0, new Intent(ACTION_PAC_REFRESH), PendingIntent.FLAG_IMMUTABLE);
         context.registerReceiver(new PacRefreshIntentReceiver(),
                 new IntentFilter(ACTION_PAC_REFRESH));
-        mConnectivityHandler = handler;
-        mProxyMessage = proxyMessage;
     }
 
     private AlarmManager getAlarmManager() {
@@ -169,38 +177,52 @@
         return mAlarmManager;
     }
 
+    @Override
+    public void addListener(IPacProxyInstalledListener listener) {
+        PermissionUtils.enforceNetworkStackPermissionOr(mContext,
+                android.Manifest.permission.NETWORK_SETTINGS);
+        mCallbacks.register(listener);
+    }
+
+    @Override
+    public void removeListener(IPacProxyInstalledListener listener) {
+        PermissionUtils.enforceNetworkStackPermissionOr(mContext,
+                android.Manifest.permission.NETWORK_SETTINGS);
+        mCallbacks.unregister(listener);
+    }
+
     /**
      * Updates the PAC Proxy Installer with current Proxy information. This is called by
-     * the ProxyTracker directly before a broadcast takes place to allow
-     * the PacProxyInstaller to indicate that the broadcast should not be sent and the
-     * PacProxyInstaller will trigger a new broadcast when it is ready.
+     * the ProxyTracker through PacProxyManager before a broadcast takes place to allow
+     * the PacProxyService to indicate that the broadcast should not be sent and the
+     * PacProxyService will trigger a new broadcast when it is ready.
      *
      * @param proxy Proxy information that is about to be broadcast.
-     * @return Returns whether the broadcast should be sent : either DO_ or DONT_SEND_BROADCAST
      */
-    public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
-        if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
-            if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
-                // Allow to send broadcast, nothing to do.
-                return DO_SEND_BROADCAST;
-            }
-            mPacUrl = proxy.getPacFileUrl();
-            mCurrentDelay = DELAY_1;
-            mHasSentBroadcast = false;
-            mHasDownloaded = false;
-            getAlarmManager().cancel(mPacRefreshIntent);
-            bind();
-            return DONT_SEND_BROADCAST;
-        } else {
-            getAlarmManager().cancel(mPacRefreshIntent);
-            synchronized (mProxyLock) {
-                mPacUrl = Uri.EMPTY;
-                mCurrentPac = null;
-                if (mProxyService != null) {
-                    unbind();
+    @Override
+    public void setCurrentProxyScriptUrl(@Nullable ProxyInfo proxy) {
+        PermissionUtils.enforceNetworkStackPermissionOr(mContext,
+                android.Manifest.permission.NETWORK_SETTINGS);
+
+        synchronized (mBroadcastStateLock) {
+            if (proxy != null && !Uri.EMPTY.equals(proxy.getPacFileUrl())) {
+                if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) return;
+                mPacUrl = proxy.getPacFileUrl();
+                mCurrentDelay = DELAY_1;
+                mHasSentBroadcast = false;
+                mHasDownloaded = false;
+                getAlarmManager().cancel(mPacRefreshIntent);
+                bind();
+            } else {
+                getAlarmManager().cancel(mPacRefreshIntent);
+                synchronized (mProxyLock) {
+                    mPacUrl = Uri.EMPTY;
+                    mCurrentPac = null;
+                    if (mProxyService != null) {
+                        unbind();
+                    }
                 }
             }
-            return DO_SEND_BROADCAST;
         }
     }
 
@@ -275,6 +297,7 @@
         getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
     }
 
+    @GuardedBy("mProxyLock")
     private void setCurrentProxyScript(String script) {
         if (mProxyService == null) {
             Log.e(TAG, "setCurrentProxyScript: no proxy service");
@@ -347,6 +370,9 @@
                             public void setProxyPort(int port) {
                                 if (mLastPort != -1) {
                                     // Always need to send if port changed
+                                    // TODO: Here lacks synchronization because this write cannot
+                                    // guarantee that it's visible from sendProxyIfNeeded() when
+                                    // it's called by a Runnable which is post by mNetThread.
                                     mHasSentBroadcast = false;
                                 }
                                 mLastPort = port;
@@ -365,8 +391,9 @@
                 }
             }
         };
-        mContext.bindService(intent, mProxyConnection,
-                Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
+        mContext.bindService(intent,
+                Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE,
+                new HandlerExecutor(mNetThreadHandler), mProxyConnection);
     }
 
     private void unbind() {
@@ -383,16 +410,28 @@
     }
 
     private void sendPacBroadcast(ProxyInfo proxy) {
-        mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
+        final int length = mCallbacks.beginBroadcast();
+        for (int i = 0; i < length; i++) {
+            final IPacProxyInstalledListener listener = mCallbacks.getBroadcastItem(i);
+            if (listener != null) {
+                try {
+                    listener.onPacProxyInstalled(null /* network */, proxy);
+                } catch (RemoteException ignored) { }
+            }
+        }
+        mCallbacks.finishBroadcast();
     }
 
-    private synchronized void sendProxyIfNeeded() {
-        if (!mHasDownloaded || (mLastPort == -1)) {
-            return;
-        }
-        if (!mHasSentBroadcast) {
-            sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort));
-            mHasSentBroadcast = true;
+    // This method must be called on mNetThreadHandler.
+    private void sendProxyIfNeeded() {
+        synchronized (mBroadcastStateLock) {
+            if (!mHasDownloaded || (mLastPort == -1)) {
+                return;
+            }
+            if (!mHasSentBroadcast) {
+                sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort));
+                mHasSentBroadcast = true;
+            }
         }
     }
 }
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 9411e33..3711679 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -31,14 +31,17 @@
 import static com.android.net.module.util.CollectionUtils.toIntArray;
 
 import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PackageManagerInternal;
 import android.net.INetd;
 import android.net.UidRange;
+import android.net.Uri;
 import android.os.Build;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
@@ -54,7 +57,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.net.module.util.CollectionUtils;
-import com.android.server.LocalServices;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -71,7 +73,7 @@
  *
  * @hide
  */
-public class PermissionMonitor implements PackageManagerInternal.PackageListObserver {
+public class PermissionMonitor {
     private static final String TAG = "PermissionMonitor";
     private static final boolean DBG = true;
     protected static final Boolean SYSTEM = Boolean.TRUE;
@@ -83,6 +85,7 @@
     private final SystemConfigManager mSystemConfigManager;
     private final INetd mNetd;
     private final Dependencies mDeps;
+    private final Context mContext;
 
     @GuardedBy("this")
     private final Set<UserHandle> mUsers = new HashSet<>();
@@ -102,6 +105,25 @@
     @GuardedBy("this")
     private final Set<Integer> mAllApps = new HashSet<>();
 
+    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+            final Uri packageData = intent.getData();
+            final String packageName =
+                    packageData != null ? packageData.getSchemeSpecificPart() : null;
+
+            if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                onPackageAdded(packageName, uid);
+            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                onPackageRemoved(packageName, uid);
+            } else {
+                Log.wtf(TAG, "received unexpected intent: " + action);
+            }
+        }
+    };
+
     /**
      * Dependencies of PermissionMonitor, for injection in tests.
      */
@@ -127,6 +149,7 @@
         mSystemConfigManager = context.getSystemService(SystemConfigManager.class);
         mNetd = netd;
         mDeps = deps;
+        mContext = context;
     }
 
     // Intended to be called only once at startup, after the system is ready. Installs a broadcast
@@ -134,12 +157,14 @@
     public synchronized void startMonitoring() {
         log("Monitoring");
 
-        PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
-        if (pmi != null) {
-            pmi.getPackageList(this);
-        } else {
-            loge("failed to get the PackageManagerInternal service");
-        }
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        intentFilter.addDataScheme("package");
+        mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */).registerReceiver(
+                mIntentReceiver, intentFilter, null /* broadcastPermission */,
+                null /* scheduler */);
+
         List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS
                 | MATCH_ANY_USER);
         if (apps == null) {
@@ -246,6 +271,13 @@
         return mApps.containsKey(uid);
     }
 
+    /**
+     * Returns whether the given uid has permission to use restricted networks.
+     */
+    public synchronized boolean hasRestrictedNetworksPermission(int uid) {
+        return Boolean.TRUE.equals(mApps.get(uid));
+    }
+
     private void update(Set<UserHandle> users, Map<Integer, Boolean> apps, boolean add) {
         List<Integer> network = new ArrayList<>();
         List<Integer> system = new ArrayList<>();
@@ -347,9 +379,10 @@
      *
      * @hide
      */
-    @Override
     public synchronized void onPackageAdded(@NonNull final String packageName, final int uid) {
-        sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
+        // TODO: Netd is using appId for checking traffic permission. Correct the methods that are
+        //  using appId instead of uid actually
+        sendPackagePermissionsForUid(UserHandle.getAppId(uid), getPermissionForUid(uid));
 
         // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
         // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
@@ -384,9 +417,10 @@
      *
      * @hide
      */
-    @Override
     public synchronized void onPackageRemoved(@NonNull final String packageName, final int uid) {
-        sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
+        // TODO: Netd is using appId for checking traffic permission. Correct the methods that are
+        //  using appId instead of uid actually
+        sendPackagePermissionsForUid(UserHandle.getAppId(uid), getPermissionForUid(uid));
 
         // If the newly-removed package falls within some VPN's uid range, update Netd with it.
         // This needs to happen before the mApps update below, since removeBypassingUids() depends
@@ -432,19 +466,6 @@
         }
     }
 
-    /**
-     * Called when a package is changed.
-     *
-     * @param packageName The name of the changed package.
-     * @param uid The uid of the changed package.
-     *
-     * @hide
-     */
-    @Override
-    public synchronized void onPackageChanged(@NonNull final String packageName, final int uid) {
-        sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
-    }
-
     private static int getNetdPermissionMask(String[] requestedPermissions,
                                              int[] requestedPermissionsFlags) {
         int permissions = 0;
diff --git a/services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java b/services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java
new file mode 100644
index 0000000..dd2815d
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java
@@ -0,0 +1,87 @@
+/*
+ * 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.NetworkCapabilities;
+import android.os.UserHandle;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A data class containing all the per-profile network preferences.
+ *
+ * A given profile can only have one preference.
+ */
+public class ProfileNetworkPreferences {
+    /**
+     * A single preference, as it applies to a given user profile.
+     */
+    public static class Preference {
+        @NonNull public final UserHandle user;
+        // Capabilities are only null when sending an object to remove the setting for a user
+        @Nullable public final NetworkCapabilities capabilities;
+
+        public Preference(@NonNull final UserHandle user,
+                @Nullable final NetworkCapabilities capabilities) {
+            this.user = user;
+            this.capabilities = null == capabilities ? null : new NetworkCapabilities(capabilities);
+        }
+
+        /** toString */
+        public String toString() {
+            return "[ProfileNetworkPreference user=" + user + " caps=" + capabilities + "]";
+        }
+    }
+
+    @NonNull public final List<Preference> preferences;
+
+    public ProfileNetworkPreferences() {
+        preferences = Collections.EMPTY_LIST;
+    }
+
+    private ProfileNetworkPreferences(@NonNull final List<Preference> list) {
+        preferences = Collections.unmodifiableList(list);
+    }
+
+    /**
+     * Returns a new object consisting of this object plus the passed preference.
+     *
+     * If a preference already exists for the same user, it will be replaced by the passed
+     * preference. Passing a Preference object containing a null capabilities object is equivalent
+     * to (and indeed, implemented as) removing the preference for this user.
+     */
+    public ProfileNetworkPreferences plus(@NonNull final Preference pref) {
+        final ArrayList<Preference> newPrefs = new ArrayList<>();
+        for (final Preference existingPref : preferences) {
+            if (!existingPref.user.equals(pref.user)) {
+                newPrefs.add(existingPref);
+            }
+        }
+        if (null != pref.capabilities) {
+            newPrefs.add(pref);
+        }
+        return new ProfileNetworkPreferences(newPrefs);
+    }
+
+    public boolean isEmpty() {
+        return preferences.isEmpty();
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index d83ff83..f572b46 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -16,10 +16,10 @@
 
 package com.android.server.connectivity;
 
-import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST;
-import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_HOST;
-import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_PAC;
-import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_PORT;
+import static android.net.ConnectivitySettingsManager.GLOBAL_HTTP_PROXY_EXCLUSION_LIST;
+import static android.net.ConnectivitySettingsManager.GLOBAL_HTTP_PROXY_HOST;
+import static android.net.ConnectivitySettingsManager.GLOBAL_HTTP_PROXY_PAC;
+import static android.net.ConnectivitySettingsManager.GLOBAL_HTTP_PROXY_PORT;
 import static android.provider.Settings.Global.HTTP_PROXY;
 
 import android.annotation.NonNull;
@@ -27,6 +27,8 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.net.Network;
+import android.net.PacProxyManager;
 import android.net.Proxy;
 import android.net.ProxyInfo;
 import android.net.Uri;
@@ -36,6 +38,7 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.net.module.util.ProxyUtils;
@@ -67,7 +70,7 @@
     // is not set. Individual networks have their own settings that override this. This member
     // is set through setDefaultProxy, which is called when the default network changes proxies
     // in its LinkProperties, or when ConnectivityService switches to a new default network, or
-    // when PacProxyInstaller resolves the proxy.
+    // when PacProxyService resolves the proxy.
     @Nullable
     @GuardedBy("mProxyLock")
     private volatile ProxyInfo mDefaultProxy = null;
@@ -77,16 +80,31 @@
 
     private final Handler mConnectivityServiceHandler;
 
-    // The object responsible for Proxy Auto Configuration (PAC).
-    @NonNull
-    private final PacProxyInstaller mPacProxyInstaller;
+    private final PacProxyManager mPacProxyManager;
+
+    private class PacProxyInstalledListener implements PacProxyManager.PacProxyInstalledListener {
+        private final int mEvent;
+
+        PacProxyInstalledListener(int event) {
+            mEvent = event;
+        }
+
+        public void onPacProxyInstalled(@Nullable Network network, @NonNull ProxyInfo proxy) {
+            mConnectivityServiceHandler
+                    .sendMessage(mConnectivityServiceHandler
+                    .obtainMessage(mEvent, new Pair<>(network, proxy)));
+        }
+    }
 
     public ProxyTracker(@NonNull final Context context,
             @NonNull final Handler connectivityServiceInternalHandler, final int pacChangedEvent) {
         mContext = context;
         mConnectivityServiceHandler = connectivityServiceInternalHandler;
-        mPacProxyInstaller = new PacProxyInstaller(
-                context, connectivityServiceInternalHandler, pacChangedEvent);
+        mPacProxyManager = context.getSystemService(PacProxyManager.class);
+
+        PacProxyInstalledListener listener = new PacProxyInstalledListener(pacChangedEvent);
+        mPacProxyManager.addPacProxyInstalledListener(
+                mConnectivityServiceHandler::post, listener);
     }
 
     // Convert empty ProxyInfo's to null as null-checks are used to determine if proxies are present
@@ -182,7 +200,7 @@
 
             if (!TextUtils.isEmpty(pacFileUrl)) {
                 mConnectivityServiceHandler.post(
-                        () -> mPacProxyInstaller.setCurrentProxyScriptUrl(proxyProperties));
+                        () -> mPacProxyManager.setCurrentProxyScriptUrl(proxyProperties));
             }
         }
     }
@@ -226,9 +244,9 @@
         final ProxyInfo defaultProxy = getDefaultProxy();
         final ProxyInfo proxyInfo = null != defaultProxy ?
                 defaultProxy : ProxyInfo.buildDirectProxy("", 0, Collections.emptyList());
+        mPacProxyManager.setCurrentProxyScriptUrl(proxyInfo);
 
-        if (mPacProxyInstaller.setCurrentProxyScriptUrl(proxyInfo)
-                == PacProxyInstaller.DONT_SEND_BROADCAST) {
+        if (!shouldSendBroadcast(proxyInfo)) {
             return;
         }
         if (DBG) Log.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
@@ -244,6 +262,10 @@
         }
     }
 
+    private boolean shouldSendBroadcast(ProxyInfo proxy) {
+        return Uri.EMPTY.equals(proxy.getPacFileUrl()) || proxy.getPort() > 0;
+    }
+
     /**
      * Sets the global proxy in memory. Also writes the values to the global settings of the device.
      *
@@ -308,10 +330,10 @@
                 return;
             }
 
-            // This call could be coming from the PacProxyInstaller, containing the port of the
+            // This call could be coming from the PacProxyService, containing the port of the
             // local proxy. If this new proxy matches the global proxy then copy this proxy to the
             // global (to get the correct local port), and send a broadcast.
-            // TODO: Switch PacProxyInstaller to have its own message to send back rather than
+            // TODO: Switch PacProxyService to have its own message to send back rather than
             // reusing EVENT_HAS_CHANGED_PROXY and this call to handleApplyDefaultProxy.
             if ((mGlobalProxy != null) && (proxyInfo != null)
                     && (!Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))
diff --git a/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java b/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java
index 0f5400d..534dbe7 100644
--- a/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java
+++ b/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java
@@ -27,6 +27,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.telephony.data.NrQosSessionAttributes;
 import android.util.Log;
 
 import java.util.Objects;
@@ -146,13 +147,23 @@
         mNetworkAgentInfo.onQosCallbackUnregistered(mAgentCallbackId);
     }
 
-    void sendEventQosSessionAvailable(final QosSession session,
+    void sendEventEpsQosSessionAvailable(final QosSession session,
             final EpsBearerQosSessionAttributes attributes) {
         try {
-            if (DBG) log("sendEventQosSessionAvailable: sending...");
+            if (DBG) log("sendEventEpsQosSessionAvailable: sending...");
             mCallback.onQosEpsBearerSessionAvailable(session, attributes);
         } catch (final RemoteException e) {
-            loge("sendEventQosSessionAvailable: remote exception", e);
+            loge("sendEventEpsQosSessionAvailable: remote exception", e);
+        }
+    }
+
+    void sendEventNrQosSessionAvailable(final QosSession session,
+            final NrQosSessionAttributes attributes) {
+        try {
+            if (DBG) log("sendEventNrQosSessionAvailable: sending...");
+            mCallback.onNrQosSessionAvailable(session, attributes);
+        } catch (final RemoteException e) {
+            loge("sendEventNrQosSessionAvailable: remote exception", e);
         }
     }
 
diff --git a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java b/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
index 8bda532..b6ab47b 100644
--- a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
+++ b/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
@@ -27,6 +27,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.telephony.data.NrQosSessionAttributes;
 import android.util.Log;
 
 import com.android.net.module.util.CollectionUtils;
@@ -179,17 +180,31 @@
     }
 
     /**
-     * Called when the NetworkAgent sends the qos session available event
+     * Called when the NetworkAgent sends the qos session available event for EPS
      *
      * @param qosCallbackId the callback id that the qos session is now available to
      * @param session the qos session that is now available
      * @param attributes the qos attributes that are now available on the qos session
      */
-    public void sendEventQosSessionAvailable(final int qosCallbackId,
+    public void sendEventEpsQosSessionAvailable(final int qosCallbackId,
             final QosSession session,
             final EpsBearerQosSessionAttributes attributes) {
-        runOnAgentConnection(qosCallbackId, "sendEventQosSessionAvailable: ",
-                ac -> ac.sendEventQosSessionAvailable(session, attributes));
+        runOnAgentConnection(qosCallbackId, "sendEventEpsQosSessionAvailable: ",
+                ac -> ac.sendEventEpsQosSessionAvailable(session, attributes));
+    }
+
+    /**
+     * Called when the NetworkAgent sends the qos session available event for NR
+     *
+     * @param qosCallbackId the callback id that the qos session is now available to
+     * @param session the qos session that is now available
+     * @param attributes the qos attributes that are now available on the qos session
+     */
+    public void sendEventNrQosSessionAvailable(final int qosCallbackId,
+            final QosSession session,
+            final NrQosSessionAttributes attributes) {
+        runOnAgentConnection(qosCallbackId, "sendEventNrQosSessionAvailable: ",
+                ac -> ac.sendEventNrQosSessionAvailable(session, attributes));
     }
 
     /**
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 124c374..e5ce4f0 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -17,11 +17,11 @@
 package com.android.server.connectivity;
 
 import static android.Manifest.permission.BIND_VPN_SERVICE;
-import static android.net.ConnectivityManager.NETID_UNSET;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.RouteInfo.RTN_THROW;
 import static android.net.RouteInfo.RTN_UNREACHABLE;
 import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN;
+import static android.os.UserHandle.PER_USER_RANGE;
 
 import static com.android.internal.util.Preconditions.checkArgument;
 import static com.android.internal.util.Preconditions.checkNotNull;
@@ -68,8 +68,8 @@
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkProvider;
 import android.net.NetworkRequest;
+import android.net.NetworkScore;
 import android.net.RouteInfo;
-import android.net.UidRange;
 import android.net.UidRangeParcel;
 import android.net.UnderlyingNetworkInfo;
 import android.net.VpnManager;
@@ -222,7 +222,7 @@
     protected NetworkAgent mNetworkAgent;
     private final Looper mLooper;
     @VisibleForTesting
-    protected final NetworkCapabilities mNetworkCapabilities;
+    protected NetworkCapabilities mNetworkCapabilities;
     private final SystemServices mSystemServices;
     private final Ikev2SessionCreator mIkev2SessionCreator;
     private final UserManager mUserManager;
@@ -459,11 +459,12 @@
         mLegacyState = LegacyVpnInfo.STATE_DISCONNECTED;
         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0 /* subtype */, NETWORKTYPE,
                 "" /* subtypeName */);
-        mNetworkCapabilities = new NetworkCapabilities();
-        mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
-        mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
-        mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
-        mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE));
+        mNetworkCapabilities = new NetworkCapabilities.Builder()
+                .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
+                .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
+                .setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE))
+                .build();
 
         loadAlwaysOnPackage();
     }
@@ -524,8 +525,10 @@
     }
 
     private void resetNetworkCapabilities() {
-        mNetworkCapabilities.setUids(null);
-        mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE));
+        mNetworkCapabilities = new NetworkCapabilities.Builder(mNetworkCapabilities)
+                .setUids(null)
+                .setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE))
+                .build();
     }
 
     /**
@@ -1123,17 +1126,19 @@
     }
 
     /**
-     * Return netId of current running VPN network.
+     * Return Network of current running VPN network.
      *
-     * @return a netId if there is a running VPN network or NETID_UNSET if there is no running VPN
+     * @return a Network if there is a running VPN network or null if there is no running VPN
      *         network or network is null.
      */
-    public synchronized int getNetId() {
+    @VisibleForTesting
+    @Nullable
+    public synchronized Network getNetwork() {
         final NetworkAgent agent = mNetworkAgent;
-        if (null == agent) return NETID_UNSET;
+        if (null == agent) return null;
         final Network network = agent.getNetwork();
-        if (null == network) return NETID_UNSET;
-        return network.getNetId();
+        if (null == network) return null;
+        return network;
     }
 
     private LinkProperties makeLinkProperties() {
@@ -1174,11 +1179,13 @@
 
         if (!allowIPv4) {
             lp.addRoute(new RouteInfo(new IpPrefix(
-                    NetworkStackConstants.IPV4_ADDR_ANY, 0), RTN_UNREACHABLE));
+                    NetworkStackConstants.IPV4_ADDR_ANY, 0), null /*gateway*/,
+                    null /*iface*/, RTN_UNREACHABLE));
         }
         if (!allowIPv6) {
             lp.addRoute(new RouteInfo(new IpPrefix(
-                    NetworkStackConstants.IPV6_ADDR_ANY, 0), RTN_UNREACHABLE));
+                    NetworkStackConstants.IPV6_ADDR_ANY, 0), null /*gateway*/,
+                    null /*iface*/, RTN_UNREACHABLE));
         }
 
         // Concatenate search domains into a string.
@@ -1234,33 +1241,39 @@
         // registered with registerDefaultNetworkCallback. This in turn protects the invariant
         // that an app calling ConnectivityManager#bindProcessToNetwork(getDefaultNetwork())
         // behaves the same as when it uses the default network.
-        mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+        final NetworkCapabilities.Builder capsBuilder =
+                new NetworkCapabilities.Builder(mNetworkCapabilities);
+        capsBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
 
         mLegacyState = LegacyVpnInfo.STATE_CONNECTING;
         updateState(DetailedState.CONNECTING, "agentConnect");
 
-        NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig();
-        networkAgentConfig.allowBypass = mConfig.allowBypass && !mLockdown;
+        final NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig.Builder()
+                .setBypassableVpn(mConfig.allowBypass && !mLockdown)
+                .build();
 
-        mNetworkCapabilities.setOwnerUid(mOwnerUID);
-        mNetworkCapabilities.setAdministratorUids(new int[] {mOwnerUID});
-        mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserId,
+        capsBuilder.setOwnerUid(mOwnerUID);
+        capsBuilder.setAdministratorUids(new int[] {mOwnerUID});
+        capsBuilder.setUids(createUserAndRestrictedProfilesRanges(mUserId,
                 mConfig.allowedApplications, mConfig.disallowedApplications));
 
-        mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(getActiveVpnType()));
+        capsBuilder.setTransportInfo(new VpnTransportInfo(getActiveVpnType()));
 
         // Only apps targeting Q and above can explicitly declare themselves as metered.
         // These VPNs are assumed metered unless they state otherwise.
         if (mIsPackageTargetingAtLeastQ && mConfig.isMetered) {
-            mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_METERED);
+            capsBuilder.removeCapability(NET_CAPABILITY_NOT_METERED);
         } else {
-            mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
+            capsBuilder.addCapability(NET_CAPABILITY_NOT_METERED);
         }
 
+        mNetworkCapabilities = capsBuilder.build();
         mNetworkAgent = new NetworkAgent(mContext, mLooper, NETWORKTYPE /* logtag */,
-                mNetworkCapabilities, lp, VPN_DEFAULT_SCORE, networkAgentConfig, mNetworkProvider) {
+                mNetworkCapabilities, lp,
+                new NetworkScore.Builder().setLegacyInt(VPN_DEFAULT_SCORE).build(),
+                networkAgentConfig, mNetworkProvider) {
             @Override
-            public void unwanted() {
+            public void onNetworkUnwanted() {
                 // We are user controlled, not driven by NetworkRequest.
             }
         };
@@ -1275,7 +1288,6 @@
         });
         mNetworkAgent.setUnderlyingNetworks((mConfig.underlyingNetworks != null)
                 ? Arrays.asList(mConfig.underlyingNetworks) : null);
-        mNetworkInfo.setIsAvailable(true);
         updateState(DetailedState.CONNECTED, "agentConnect");
     }
 
@@ -1346,7 +1358,7 @@
         String oldInterface = mInterface;
         Connection oldConnection = mConnection;
         NetworkAgent oldNetworkAgent = mNetworkAgent;
-        Set<UidRange> oldUsers = mNetworkCapabilities.getUids();
+        Set<Range<Integer>> oldUsers = mNetworkCapabilities.getUids();
 
         // Configure the interface. Abort if any of these steps fails.
         ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
@@ -1421,7 +1433,8 @@
             // restore old state
             mConfig = oldConfig;
             mConnection = oldConnection;
-            mNetworkCapabilities.setUids(oldUsers);
+            mNetworkCapabilities =
+                    new NetworkCapabilities.Builder(mNetworkCapabilities).setUids(oldUsers).build();
             mNetworkAgent = oldNetworkAgent;
             mInterface = oldInterface;
             throw e;
@@ -1452,7 +1465,7 @@
     }
 
     /**
-     * Creates a {@link Set} of non-intersecting {@link UidRange} objects including all UIDs
+     * Creates a {@link Set} of non-intersecting {@code Range<Integer>} objects including all UIDs
      * associated with one user, and any restricted profiles attached to that user.
      *
      * <p>If one of {@param allowedApplications} or {@param disallowedApplications} is provided,
@@ -1465,10 +1478,10 @@
      * @param disallowedApplications (optional) List of applications to deny.
      */
     @VisibleForTesting
-    Set<UidRange> createUserAndRestrictedProfilesRanges(@UserIdInt int userId,
+    Set<Range<Integer>> createUserAndRestrictedProfilesRanges(@UserIdInt int userId,
             @Nullable List<String> allowedApplications,
             @Nullable List<String> disallowedApplications) {
-        final Set<UidRange> ranges = new ArraySet<>();
+        final Set<Range<Integer>> ranges = new ArraySet<>();
 
         // Assign the top-level user to the set of ranges
         addUserToRanges(ranges, userId, allowedApplications, disallowedApplications);
@@ -1492,20 +1505,20 @@
     }
 
     /**
-     * Updates a {@link Set} of non-intersecting {@link UidRange} objects to include all UIDs
+     * Updates a {@link Set} of non-intersecting {@code Range<Integer>} objects to include all UIDs
      * associated with one user.
      *
      * <p>If one of {@param allowedApplications} or {@param disallowedApplications} is provided,
      * the UID ranges will match the app allowlist or denylist specified there. Otherwise, all UIDs
      * in the user will be included.
      *
-     * @param ranges {@link Set} of {@link UidRange}s to which to add.
+     * @param ranges {@link Set} of {@code Range<Integer>}s to which to add.
      * @param userId The userId to add to {@param ranges}.
      * @param allowedApplications (optional) allowlist of applications to include.
      * @param disallowedApplications (optional) denylist of applications to exclude.
      */
     @VisibleForTesting
-    void addUserToRanges(@NonNull Set<UidRange> ranges, @UserIdInt int userId,
+    void addUserToRanges(@NonNull Set<Range<Integer>> ranges, @UserIdInt int userId,
             @Nullable List<String> allowedApplications,
             @Nullable List<String> disallowedApplications) {
         if (allowedApplications != null) {
@@ -1515,40 +1528,41 @@
                 if (start == -1) {
                     start = uid;
                 } else if (uid != stop + 1) {
-                    ranges.add(new UidRange(start, stop));
+                    ranges.add(new Range<Integer>(start, stop));
                     start = uid;
                 }
                 stop = uid;
             }
-            if (start != -1) ranges.add(new UidRange(start, stop));
+            if (start != -1) ranges.add(new Range<Integer>(start, stop));
         } else if (disallowedApplications != null) {
             // Add all ranges for user skipping UIDs for disallowedApplications.
-            final UidRange userRange = UidRange.createForUser(UserHandle.of(userId));
-            int start = userRange.start;
+            final Range<Integer> userRange = createUidRangeForUser(userId);
+            int start = userRange.getLower();
             for (int uid : getAppsUids(disallowedApplications, userId)) {
                 if (uid == start) {
                     start++;
                 } else {
-                    ranges.add(new UidRange(start, uid - 1));
+                    ranges.add(new Range<Integer>(start, uid - 1));
                     start = uid + 1;
                 }
             }
-            if (start <= userRange.stop) ranges.add(new UidRange(start, userRange.stop));
+            if (start <= userRange.getUpper()) {
+                ranges.add(new Range<Integer>(start, userRange.getUpper()));
+            }
         } else {
             // Add all UIDs for the user.
-            ranges.add(UidRange.createForUser(UserHandle.of(userId)));
+            ranges.add(createUidRangeForUser(userId));
         }
     }
 
     // Returns the subset of the full list of active UID ranges the VPN applies to (mVpnUsers) that
     // apply to userId.
-    private static List<UidRange> uidRangesForUser(int userId, Set<UidRange> existingRanges) {
-        // UidRange#createForUser returns the entire range of UIDs available to a macro-user.
-        // This is something like 0-99999 ; {@see UserHandle#PER_USER_RANGE}
-        final UidRange userRange = UidRange.createForUser(UserHandle.of(userId));
-        final List<UidRange> ranges = new ArrayList<>();
-        for (UidRange range : existingRanges) {
-            if (userRange.containsRange(range)) {
+    private static List<Range<Integer>> uidRangesForUser(int userId,
+            Set<Range<Integer>> existingRanges) {
+        final Range<Integer> userRange = createUidRangeForUser(userId);
+        final List<Range<Integer>> ranges = new ArrayList<>();
+        for (Range<Integer> range : existingRanges) {
+            if (userRange.contains(range)) {
                 ranges.add(range);
             }
         }
@@ -1565,12 +1579,13 @@
         UserInfo user = mUserManager.getUserInfo(userId);
         if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
             synchronized(Vpn.this) {
-                final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
+                final Set<Range<Integer>> existingRanges = mNetworkCapabilities.getUids();
                 if (existingRanges != null) {
                     try {
                         addUserToRanges(existingRanges, userId, mConfig.allowedApplications,
                                 mConfig.disallowedApplications);
-                        mNetworkCapabilities.setUids(existingRanges);
+                        mNetworkCapabilities = new NetworkCapabilities.Builder(mNetworkCapabilities)
+                                .setUids(existingRanges).build();
                     } catch (Exception e) {
                         Log.wtf(TAG, "Failed to add restricted user to owner", e);
                     }
@@ -1593,13 +1608,14 @@
         UserInfo user = mUserManager.getUserInfo(userId);
         if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
             synchronized(Vpn.this) {
-                final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
+                final Set<Range<Integer>> existingRanges = mNetworkCapabilities.getUids();
                 if (existingRanges != null) {
                     try {
-                        final List<UidRange> removedRanges =
+                        final List<Range<Integer>> removedRanges =
                                 uidRangesForUser(userId, existingRanges);
                         existingRanges.removeAll(removedRanges);
-                        mNetworkCapabilities.setUids(existingRanges);
+                        mNetworkCapabilities = new NetworkCapabilities.Builder(mNetworkCapabilities)
+                                .setUids(existingRanges).build();
                     } catch (Exception e) {
                         Log.wtf(TAG, "Failed to remove restricted user to owner", e);
                     }
@@ -1657,7 +1673,7 @@
         final Set<UidRangeParcel> rangesToRemove = new ArraySet<>(mBlockedUidsAsToldToConnectivity);
         final Set<UidRangeParcel> rangesToAdd;
         if (enforce) {
-            final Set<UidRange> restrictedProfilesRanges =
+            final Set<Range<Integer>> restrictedProfilesRanges =
                     createUserAndRestrictedProfilesRanges(mUserId,
                     /* allowedApplications */ null,
                     /* disallowedApplications */ exemptedPackages);
@@ -1666,11 +1682,12 @@
             // The UID range of the first user (0-99999) would block the IPSec traffic, which comes
             // directly from the kernel and is marked as uid=0. So we adjust the range to allow
             // it through (b/69873852).
-            for (UidRange range : restrictedProfilesRanges) {
-                if (range.start == 0 && range.stop != 0) {
-                    rangesThatShouldBeBlocked.add(new UidRangeParcel(1, range.stop));
-                } else if (range.start != 0) {
-                    rangesThatShouldBeBlocked.add(new UidRangeParcel(range.start, range.stop));
+            for (Range<Integer> range : restrictedProfilesRanges) {
+                if (range.getLower() == 0 && range.getUpper() != 0) {
+                    rangesThatShouldBeBlocked.add(new UidRangeParcel(1, range.getUpper()));
+                } else if (range.getLower() != 0) {
+                    rangesThatShouldBeBlocked.add(
+                            new UidRangeParcel(range.getLower(), range.getUpper()));
                 }
             }
 
@@ -1692,12 +1709,12 @@
     }
 
     /**
-     * Tell ConnectivityService to add or remove a list of {@link UidRange}s to the list of UIDs
-     * that are only allowed to make connections through sockets that have had {@code protect()}
-     * called on them.
+     * Tell ConnectivityService to add or remove a list of {@link UidRangeParcel}s to the list of
+     * UIDs that are only allowed to make connections through sockets that have had
+     * {@code protect()} called on them.
      *
      * @param enforce {@code true} to add to the denylist, {@code false} to remove.
-     * @param ranges {@link Collection} of {@link UidRange}s to add (if {@param enforce} is
+     * @param ranges {@link Collection} of {@link UidRangeParcel}s to add (if {@param enforce} is
      *               {@code true}) or to remove.
      * @return {@code true} if all of the UIDs were added/removed. {@code false} otherwise,
      *         including added ranges that already existed or removed ones that didn't.
@@ -1842,22 +1859,13 @@
     /**
      * Updates underlying network set.
      */
-    public synchronized boolean setUnderlyingNetworks(Network[] networks) {
+    public synchronized boolean setUnderlyingNetworks(@Nullable Network[] networks) {
         if (!isCallerEstablishedOwnerLocked()) {
             return false;
         }
-        if (networks == null) {
-            mConfig.underlyingNetworks = null;
-        } else {
-            mConfig.underlyingNetworks = new Network[networks.length];
-            for (int i = 0; i < networks.length; ++i) {
-                if (networks[i] == null) {
-                    mConfig.underlyingNetworks[i] = null;
-                } else {
-                    mConfig.underlyingNetworks[i] = new Network(networks[i].getNetId());
-                }
-            }
-        }
+        // Make defensive copy since the content of array might be altered by the caller.
+        mConfig.underlyingNetworks =
+                (networks != null) ? Arrays.copyOf(networks, networks.length) : null;
         mNetworkAgent.setUnderlyingNetworks((mConfig.underlyingNetworks != null)
                 ? Arrays.asList(mConfig.underlyingNetworks) : null);
         return true;
@@ -1879,7 +1887,12 @@
         if (!isRunningLocked()) {
             return false;
         }
-        return mNetworkCapabilities.appliesToUid(uid);
+        final Set<Range<Integer>> uids = mNetworkCapabilities.getUids();
+        if (uids == null) return true;
+        for (final Range<Integer> range : uids) {
+            if (range.contains(uid)) return true;
+        }
+        return false;
     }
 
     /**
@@ -2696,7 +2709,8 @@
 
                     mConfig.routes.clear();
                     for (final RouteInfo route : oldRoutes) {
-                        mConfig.routes.add(new RouteInfo(route.getDestination(), RTN_UNREACHABLE));
+                        mConfig.routes.add(new RouteInfo(route.getDestination(), null /*gateway*/,
+                                null /*iface*/, RTN_UNREACHABLE));
                     }
                     if (mNetworkAgent != null) {
                         mNetworkAgent.sendLinkProperties(makeLinkProperties());
@@ -3035,10 +3049,12 @@
                 // Add a throw route for the VPN server endpoint, if one was specified.
                 if (endpointAddress instanceof Inet4Address) {
                     mConfig.routes.add(new RouteInfo(
-                            new IpPrefix(endpointAddress, 32), RTN_THROW));
+                            new IpPrefix(endpointAddress, 32), null /*gateway*/,
+                            null /*iface*/, RTN_THROW));
                 } else if (endpointAddress instanceof Inet6Address) {
                     mConfig.routes.add(new RouteInfo(
-                            new IpPrefix(endpointAddress, 128), RTN_THROW));
+                            new IpPrefix(endpointAddress, 128), null /*gateway*/,
+                            null /*iface*/, RTN_THROW));
                 } else {
                     Log.e(TAG, "Unknown IP address family for VPN endpoint: "
                             + endpointAddress);
@@ -3338,4 +3354,12 @@
                     firstChildSessionCallback);
         }
     }
+
+    /**
+     * Returns the entire range of UIDs available to a macro-user. This is something like 0-99999.
+     */
+    @VisibleForTesting
+    static Range<Integer> createUidRangeForUser(int userId) {
+        return new Range<Integer>(userId * PER_USER_RANGE, (userId + 1) * PER_USER_RANGE - 1);
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
index fa03e59..47eb3eb 100644
--- a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
+++ b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
@@ -405,7 +405,8 @@
         for (final IkeTrafficSelector selector : trafficSelectors) {
             for (final IpPrefix prefix :
                     new IpRange(selector.startingAddress, selector.endingAddress).asIpPrefixes()) {
-                routes.add(new RouteInfo(prefix, null));
+                routes.add(new RouteInfo(prefix, null /*gateway*/, null /*iface*/,
+                        RouteInfo.RTN_UNICAST));
             }
         }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index f658e33..58a7025 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -619,8 +619,10 @@
             }
         }
 
-        addAndStartAction(new NewDeviceAction(this, activeSource.logicalAddress,
-                activeSource.physicalAddress, deviceType));
+        if (!mService.isPowerStandbyOrTransient()) {
+            addAndStartAction(new NewDeviceAction(this, activeSource.logicalAddress,
+                    activeSource.physicalAddress, deviceType));
+        }
     }
 
     private boolean handleNewDeviceAtTheTailOfActivePath(int path) {
@@ -796,10 +798,12 @@
     @ServiceThreadOnly
     void onNewAvrAdded(HdmiDeviceInfo avr) {
         assertRunOnServiceThread();
-        addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress()));
-        if (isConnected(avr.getPortId()) && isArcFeatureEnabled(avr.getPortId())
-                && !hasAction(SetArcTransmissionStateAction.class)) {
-            startArcAction(true);
+        if (!mService.isPowerStandbyOrTransient()) {
+            addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress()));
+            if (isConnected(avr.getPortId()) && isArcFeatureEnabled(avr.getPortId())
+                    && !hasAction(SetArcTransmissionStateAction.class)) {
+                startArcAction(true);
+            }
         }
     }
 
@@ -1656,6 +1660,7 @@
         // Remove recording actions.
         removeAction(OneTouchRecordAction.class);
         removeAction(TimerRecordingAction.class);
+        removeAction(NewDeviceAction.class);
 
         disableSystemAudioIfExist();
         disableArcIfExist();
@@ -1696,12 +1701,20 @@
         setArcStatus(false);
 
         // Seq #44.
-        removeAction(RequestArcInitiationAction.class);
+        removeAllRunningArcAction();
         if (!hasAction(RequestArcTerminationAction.class) && isArcEstablished()) {
             addAndStartAction(new RequestArcTerminationAction(this, avr.getLogicalAddress()));
         }
     }
 
+    @ServiceThreadOnly
+    private void removeAllRunningArcAction() {
+        // Running or pending actions make TV fail to broadcast <Standby> to connected devices
+        removeAction(RequestArcTerminationAction.class);
+        removeAction(RequestArcInitiationAction.class);
+        removeAction(SetArcTransmissionStateAction.class);
+    }
+
     @Override
     @ServiceThreadOnly
     protected void onStandby(boolean initiatedByCec, int standbyAction) {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 294d7e2..0215188 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -92,7 +92,6 @@
 import android.security.AndroidKeyStoreMaintenance;
 import android.security.Authorization;
 import android.security.KeyStore;
-import android.security.keystore.AndroidKeyStoreProvider;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeyProtection;
 import android.security.keystore.UserNotAuthenticatedException;
@@ -157,7 +156,6 @@
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Objects;
-import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -264,13 +262,7 @@
 
         @Override
         public void onStart() {
-            Optional<Boolean> keystore2_enabled =
-                    android.sysprop.Keystore2Properties.keystore2_enabled();
-            if (keystore2_enabled.isPresent() && keystore2_enabled.get()) {
-                android.security.keystore2.AndroidKeyStoreProvider.install();
-            } else {
-                AndroidKeyStoreProvider.install();
-            }
+            android.security.keystore2.AndroidKeyStoreProvider.install();
             mLockSettingsService = new LockSettingsService(getContext());
             publishBinderService("lock_settings", mLockSettingsService);
         }
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 48382a9..76ecc1a 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -15,14 +15,17 @@
  */
 
 package com.android.server.locksettings;
+
 import static android.os.UserHandle.USER_SYSTEM;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.os.Handler;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.UserManager;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
@@ -35,6 +38,8 @@
 import com.android.internal.widget.RebootEscrowListener;
 
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Date;
@@ -64,6 +69,24 @@
     @VisibleForTesting
     public static final String REBOOT_ESCROW_ARMED_KEY = "reboot_escrow_armed_count";
 
+    static final String REBOOT_ESCROW_KEY_ARMED_TIMESTAMP = "reboot_escrow_key_stored_timestamp";
+    static final String REBOOT_ESCROW_KEY_PROVIDER = "reboot_escrow_key_provider";
+
+    /**
+     * The verified boot 2.0 vbmeta digest of the current slot, the property value is always
+     * available after boot.
+     */
+    static final String VBMETA_DIGEST_PROP_NAME = "ro.boot.vbmeta.digest";
+    /**
+     * The system prop contains vbmeta digest of the inactive slot. The build property is set after
+     * an OTA update. RebootEscrowManager will store it in disk before the OTA reboot, so the value
+     * is available for vbmeta digest verification after the device reboots.
+     */
+    static final String OTHER_VBMETA_DIGEST_PROP_NAME = "ota.other.vbmeta_digest";
+    static final String REBOOT_ESCROW_KEY_VBMETA_DIGEST = "reboot_escrow_key_vbmeta_digest";
+    static final String REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST =
+            "reboot_escrow_key_other_vbmeta_digest";
+
     /**
      * Number of boots until we consider the escrow data to be stale for the purposes of metrics.
      * <p>
@@ -84,6 +107,31 @@
     private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT = 3;
     private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS = 30;
 
+    @IntDef(prefix = {"ERROR_"}, value = {
+            ERROR_NONE,
+            ERROR_UNKNOWN,
+            ERROR_NO_PROVIDER,
+            ERROR_LOAD_ESCROW_KEY,
+            ERROR_RETRY_COUNT_EXHAUSTED,
+            ERROR_UNLOCK_ALL_USERS,
+            ERROR_PROVIDER_MISMATCH,
+            ERROR_KEYSTORE_FAILURE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface RebootEscrowErrorCode {
+    }
+
+    static final int ERROR_NONE = 0;
+    static final int ERROR_UNKNOWN = 1;
+    static final int ERROR_NO_PROVIDER = 2;
+    static final int ERROR_LOAD_ESCROW_KEY = 3;
+    static final int ERROR_RETRY_COUNT_EXHAUSTED = 4;
+    static final int ERROR_UNLOCK_ALL_USERS = 5;
+    static final int ERROR_PROVIDER_MISMATCH = 6;
+    static final int ERROR_KEYSTORE_FAILURE = 7;
+
+    private @RebootEscrowErrorCode int mLoadEscrowDataErrorCode = ERROR_NONE;
+
     /**
      * Logs events for later debugging in bugreports.
      */
@@ -144,8 +192,7 @@
 
         private RebootEscrowProviderInterface createRebootEscrowProvider() {
             RebootEscrowProviderInterface rebootEscrowProvider;
-            if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
-                    "server_based_ror_enabled", false)) {
+            if (serverBasedResumeOnReboot()) {
                 Slog.i(TAG, "Using server based resume on reboot");
                 rebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mContext, mStorage);
             } else {
@@ -166,6 +213,11 @@
             handler.postDelayed(runnable, delayMillis);
         }
 
+        public boolean serverBasedResumeOnReboot() {
+            return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
+                    "server_based_ror_enabled", false);
+        }
+
         public Context getContext() {
             return mContext;
         }
@@ -193,6 +245,10 @@
                     0);
         }
 
+        public long getCurrentTimeMillis() {
+            return System.currentTimeMillis();
+        }
+
         public int getLoadEscrowDataRetryLimit() {
             return DeviceConfig.getInt(DeviceConfig.NAMESPACE_OTA,
                     "load_escrow_data_retry_count", DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT);
@@ -204,15 +260,22 @@
                     DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS);
         }
 
-        public void reportMetric(boolean success) {
-            // TODO(b/179105110) design error code; and report the true value for other fields.
-            FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, 0, 1, 1,
-                    -1, 0, -1);
+        public void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount,
+                int escrowDurationInSeconds, int vbmetaDigestStatus,
+                int durationSinceBootCompleteInSeconds) {
+            FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, success,
+                    errorCode, serviceType, attemptCount, escrowDurationInSeconds,
+                    vbmetaDigestStatus, durationSinceBootCompleteInSeconds);
         }
 
         public RebootEscrowEventLog getEventLog() {
             return new RebootEscrowEventLog();
         }
+
+        public String getVbmetaDigest(boolean other) {
+            return other ? SystemProperties.get(OTHER_VBMETA_DIGEST_PROP_NAME)
+                    : SystemProperties.get(VBMETA_DIGEST_PROP_NAME);
+        }
     }
 
     RebootEscrowManager(Context context, Callbacks callbacks, LockSettingsStorage storage) {
@@ -230,7 +293,7 @@
         mKeyStoreManager = injector.getKeyStoreManager();
     }
 
-    private void onGetRebootEscrowKeyFailed(List<UserInfo> users) {
+    private void onGetRebootEscrowKeyFailed(List<UserInfo> users, int attemptCount) {
         Slog.w(TAG, "Had reboot escrow data for users, but no key; removing escrow storage.");
         for (UserInfo user : users) {
             mStorage.removeRebootEscrow(user.id);
@@ -238,7 +301,7 @@
 
         // Clear the old key in keystore.
         mKeyStoreManager.clearKeyStoreEncryptionKey();
-        onEscrowRestoreComplete(false);
+        onEscrowRestoreComplete(false, attemptCount);
     }
 
     void loadRebootEscrowDataIfAvailable(Handler retryHandler) {
@@ -251,6 +314,9 @@
         }
 
         if (rebootEscrowUsers.isEmpty()) {
+            Slog.i(TAG, "No reboot escrow data found for users,"
+                    + " skipping loading escrow data");
+            clearMetricsStorage();
             return;
         }
 
@@ -274,7 +340,8 @@
         }
 
         Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts");
-        onGetRebootEscrowKeyFailed(users);
+        mLoadEscrowDataErrorCode = ERROR_RETRY_COUNT_EXHAUSTED;
+        onGetRebootEscrowKeyFailed(users, attemptNumber);
     }
 
     void loadRebootEscrowDataWithRetry(Handler retryHandler, int attemptNumber,
@@ -297,7 +364,18 @@
         }
 
         if (escrowKey == null) {
-            onGetRebootEscrowKeyFailed(users);
+            if (mLoadEscrowDataErrorCode == ERROR_NONE) {
+                // Specifically check if the RoR provider has changed after reboot.
+                int providerType = mInjector.serverBasedResumeOnReboot()
+                        ? RebootEscrowProviderInterface.TYPE_SERVER_BASED
+                        : RebootEscrowProviderInterface.TYPE_HAL;
+                if (providerType != mStorage.getInt(REBOOT_ESCROW_KEY_PROVIDER, -1, USER_SYSTEM)) {
+                    mLoadEscrowDataErrorCode = ERROR_PROVIDER_MISMATCH;
+                } else {
+                    mLoadEscrowDataErrorCode = ERROR_LOAD_ESCROW_KEY;
+                }
+            }
+            onGetRebootEscrowKeyFailed(users, attemptNumber + 1);
             return;
         }
 
@@ -311,17 +389,82 @@
         // Clear the old key in keystore. A new key will be generated by new RoR requests.
         mKeyStoreManager.clearKeyStoreEncryptionKey();
 
-        onEscrowRestoreComplete(allUsersUnlocked);
+        if (!allUsersUnlocked && mLoadEscrowDataErrorCode == ERROR_NONE) {
+            mLoadEscrowDataErrorCode = ERROR_UNLOCK_ALL_USERS;
+        }
+        onEscrowRestoreComplete(allUsersUnlocked, attemptNumber + 1);
     }
 
-    private void onEscrowRestoreComplete(boolean success) {
-        int previousBootCount = mStorage.getInt(REBOOT_ESCROW_ARMED_KEY, -1, USER_SYSTEM);
+    private void clearMetricsStorage() {
         mStorage.removeKey(REBOOT_ESCROW_ARMED_KEY, USER_SYSTEM);
+        mStorage.removeKey(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, USER_SYSTEM);
+        mStorage.removeKey(REBOOT_ESCROW_KEY_VBMETA_DIGEST, USER_SYSTEM);
+        mStorage.removeKey(REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST, USER_SYSTEM);
+        mStorage.removeKey(REBOOT_ESCROW_KEY_PROVIDER, USER_SYSTEM);
+    }
+
+    private int getVbmetaDigestStatusOnRestoreComplete() {
+        String currentVbmetaDigest = mInjector.getVbmetaDigest(false);
+        String vbmetaDigestStored = mStorage.getString(REBOOT_ESCROW_KEY_VBMETA_DIGEST,
+                "", USER_SYSTEM);
+        String vbmetaDigestOtherStored = mStorage.getString(REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST,
+                "", USER_SYSTEM);
+
+        // The other vbmeta digest is never set, assume no slot switch is attempted.
+        if (vbmetaDigestOtherStored.isEmpty()) {
+            if (currentVbmetaDigest.equals(vbmetaDigestStored)) {
+                return FrameworkStatsLog
+                        .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MATCH_EXPECTED_SLOT;
+            }
+            return FrameworkStatsLog
+                    .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MISMATCH;
+        }
+
+        // The other vbmeta digest is set, we expect to boot into the new slot.
+        if (currentVbmetaDigest.equals(vbmetaDigestOtherStored)) {
+            return FrameworkStatsLog
+                    .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MATCH_EXPECTED_SLOT;
+        } else if (currentVbmetaDigest.equals(vbmetaDigestStored)) {
+            return FrameworkStatsLog
+                    .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MATCH_FALLBACK_SLOT;
+        }
+        return FrameworkStatsLog
+                .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MISMATCH;
+    }
+
+    private void reportMetricOnRestoreComplete(boolean success, int attemptCount) {
+        int serviceType = mInjector.serverBasedResumeOnReboot()
+                ? FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED__TYPE__SERVER_BASED
+                : FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED__TYPE__HAL;
+
+        long armedTimestamp = mStorage.getLong(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, -1,
+                USER_SYSTEM);
+        int escrowDurationInSeconds = -1;
+        long currentTimeStamp = mInjector.getCurrentTimeMillis();
+        if (armedTimestamp != -1 && currentTimeStamp > armedTimestamp) {
+            escrowDurationInSeconds = (int) (currentTimeStamp - armedTimestamp) / 1000;
+        }
+
+        int vbmetaDigestStatus = getVbmetaDigestStatusOnRestoreComplete();
+        if (!success && mLoadEscrowDataErrorCode == ERROR_NONE) {
+            mLoadEscrowDataErrorCode = ERROR_UNKNOWN;
+        }
+
+        // TODO(179105110) report the duration since boot complete.
+        mInjector.reportMetric(success, mLoadEscrowDataErrorCode, serviceType, attemptCount,
+                escrowDurationInSeconds, vbmetaDigestStatus, -1);
+
+        mLoadEscrowDataErrorCode = ERROR_NONE;
+    }
+
+    private void onEscrowRestoreComplete(boolean success, int attemptCount) {
+        int previousBootCount = mStorage.getInt(REBOOT_ESCROW_ARMED_KEY, -1, USER_SYSTEM);
 
         int bootCountDelta = mInjector.getBootCount() - previousBootCount;
         if (success || (previousBootCount != -1 && bootCountDelta <= BOOT_COUNT_TOLERANCE)) {
-            mInjector.reportMetric(success);
+            reportMetricOnRestoreComplete(success, attemptCount);
         }
+        clearMetricsStorage();
     }
 
     private RebootEscrowKey getAndClearRebootEscrowKey(SecretKey kk) throws IOException {
@@ -329,6 +472,14 @@
         if (rebootEscrowProvider == null) {
             Slog.w(TAG,
                     "Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
+            mLoadEscrowDataErrorCode = ERROR_NO_PROVIDER;
+            return null;
+        }
+
+        // Server based RoR always need the decryption key from keystore.
+        if (rebootEscrowProvider.getType() == RebootEscrowProviderInterface.TYPE_SERVER_BASED
+                && kk == null) {
+            mLoadEscrowDataErrorCode = ERROR_KEYSTORE_FAILURE;
             return null;
         }
 
@@ -434,7 +585,7 @@
             return;
         }
 
-        mStorage.removeKey(REBOOT_ESCROW_ARMED_KEY, USER_SYSTEM);
+        clearMetricsStorage();
         rebootEscrowProvider.clearRebootEscrowKey();
 
         List<UserInfo> users = mUserManager.getUsers();
@@ -457,6 +608,9 @@
             return false;
         }
 
+        int actualProviderType = rebootEscrowProvider.getType();
+        // TODO(b/183140900) Fail the reboot if provider type mismatches.
+
         RebootEscrowKey escrowKey;
         synchronized (mKeyGenerationLock) {
             escrowKey = mPendingRebootEscrowKey;
@@ -476,6 +630,14 @@
         boolean armedRebootEscrow = rebootEscrowProvider.storeRebootEscrowKey(escrowKey, kk);
         if (armedRebootEscrow) {
             mStorage.setInt(REBOOT_ESCROW_ARMED_KEY, mInjector.getBootCount(), USER_SYSTEM);
+            mStorage.setLong(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, mInjector.getCurrentTimeMillis(),
+                    USER_SYSTEM);
+            // Store the vbmeta digest of both slots.
+            mStorage.setString(REBOOT_ESCROW_KEY_VBMETA_DIGEST, mInjector.getVbmetaDigest(false),
+                    USER_SYSTEM);
+            mStorage.setString(REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST,
+                    mInjector.getVbmetaDigest(true), USER_SYSTEM);
+            mStorage.setInt(REBOOT_ESCROW_KEY_PROVIDER, actualProviderType, USER_SYSTEM);
             mEventLog.addEntry(RebootEscrowEvent.SET_ARMED_STATUS);
         }
 
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java
index 4b00772..e8f6f4a 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java
@@ -60,6 +60,11 @@
     }
 
     @Override
+    public int getType() {
+        return TYPE_HAL;
+    }
+
+    @Override
     public boolean hasRebootEscrowSupport() {
         return mInjector.getRebootEscrow() != null;
     }
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java
index af6faad..e106d81 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java
@@ -16,7 +16,11 @@
 
 package com.android.server.locksettings;
 
+import android.annotation.IntDef;
+
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 import javax.crypto.SecretKey;
 
@@ -28,6 +32,21 @@
  * @hide
  */
 public interface RebootEscrowProviderInterface {
+    @IntDef(prefix = {"TYPE_"}, value = {
+            TYPE_HAL,
+            TYPE_SERVER_BASED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface RebootEscrowProviderType {
+    }
+    int TYPE_HAL = 0;
+    int TYPE_SERVER_BASED = 1;
+
+    /**
+     * Returns the reboot escrow provider type.
+     */
+    @RebootEscrowProviderType int getType();
+
     /**
      * Returns true if the secure store/discard of reboot escrow key is supported.
      */
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
index 697bf08..2866987 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
@@ -95,6 +95,11 @@
     }
 
     @Override
+    public int getType() {
+        return TYPE_SERVER_BASED;
+    }
+
+    @Override
     public boolean hasRebootEscrowSupport() {
         return mInjector.getServiceConnection() != null;
     }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformDecryptionKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformDecryptionKey.java
index 35571f1..e75aae1 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformDecryptionKey.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformDecryptionKey.java
@@ -16,7 +16,7 @@
 
 package com.android.server.locksettings.recoverablekeystore;
 
-import android.security.keystore.AndroidKeyStoreSecretKey;
+import javax.crypto.SecretKey;
 
 /**
  * Used to unwrap recoverable keys before syncing them with remote storage.
@@ -30,7 +30,7 @@
 public class PlatformDecryptionKey {
 
     private final int mGenerationId;
-    private final AndroidKeyStoreSecretKey mKey;
+    private final SecretKey mKey;
 
     /**
      * A new instance.
@@ -40,7 +40,7 @@
      *
      * @hide
      */
-    public PlatformDecryptionKey(int generationId, AndroidKeyStoreSecretKey key) {
+    public PlatformDecryptionKey(int generationId, SecretKey key) {
         mGenerationId = generationId;
         mKey = key;
     }
@@ -59,7 +59,7 @@
      *
      * @hide
      */
-    public AndroidKeyStoreSecretKey getKey() {
+    public SecretKey getKey() {
         return mKey;
     }
 }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformEncryptionKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformEncryptionKey.java
index 38f5b45..ee33446 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformEncryptionKey.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformEncryptionKey.java
@@ -16,7 +16,7 @@
 
 package com.android.server.locksettings.recoverablekeystore;
 
-import android.security.keystore.AndroidKeyStoreSecretKey;
+import javax.crypto.SecretKey;
 
 /**
  * Private key stored in AndroidKeyStore. Used to wrap recoverable keys before writing them to disk.
@@ -33,7 +33,7 @@
 public class PlatformEncryptionKey {
 
     private final int mGenerationId;
-    private final AndroidKeyStoreSecretKey mKey;
+    private final SecretKey mKey;
 
     /**
      * A new instance.
@@ -41,7 +41,7 @@
      * @param generationId The generation ID of the key.
      * @param key The secret key handle. Can be used to encrypt WITHOUT requiring screen unlock.
      */
-    public PlatformEncryptionKey(int generationId, AndroidKeyStoreSecretKey key) {
+    public PlatformEncryptionKey(int generationId, SecretKey key) {
         mGenerationId = generationId;
         mKey = key;
     }
@@ -56,7 +56,7 @@
     /**
      * Returns the actual key, which can only be used to encrypt.
      */
-    public AndroidKeyStoreSecretKey getKey() {
+    public SecretKey getKey() {
         return mKey;
     }
 }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
index 202dfe7..5e06205 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
@@ -21,7 +21,6 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.security.GateKeeper;
-import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyPermanentlyInvalidatedException;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeyProtection;
@@ -237,7 +236,7 @@
         if (!isKeyLoaded(userId, generationId)) {
             throw new UnrecoverableKeyException("KeyStore doesn't contain key " + alias);
         }
-        AndroidKeyStoreSecretKey key = (AndroidKeyStoreSecretKey) mKeyStore.getKey(
+        SecretKey key = (SecretKey) mKeyStore.getKey(
                 alias, /*password=*/ null);
         return new PlatformEncryptionKey(generationId, key);
     }
@@ -289,7 +288,7 @@
         if (!isKeyLoaded(userId, generationId)) {
             throw new UnrecoverableKeyException("KeyStore doesn't contain key " + alias);
         }
-        AndroidKeyStoreSecretKey key = (AndroidKeyStoreSecretKey) mKeyStore.getKey(
+        SecretKey key = (SecretKey) mKeyStore.getKey(
                 alias, /*password=*/ null);
         return new PlatformDecryptionKey(generationId, key);
     }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index aee0947..06a09ae 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -38,6 +38,15 @@
 import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_ADMIN_DISABLED;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_MASK;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED;
+import static android.net.ConnectivityManager.BLOCKED_REASON_APP_STANDBY;
+import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER;
+import static android.net.ConnectivityManager.BLOCKED_REASON_DOZE;
+import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
+import static android.net.ConnectivityManager.BLOCKED_REASON_RESTRICTED_MODE;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
@@ -56,6 +65,16 @@
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.SNOOZE_NEVER;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
+import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_FOREGROUND;
+import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_MASK;
+import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_SYSTEM;
+import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_USER_EXEMPTED;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_FOREGROUND;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_NONE;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_ALLOWLIST;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM;
 import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
 import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS;
@@ -155,12 +174,10 @@
 import android.database.ContentObserver;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
-import android.net.IConnectivityManager;
 import android.net.INetworkManagementEventObserver;
 import android.net.INetworkPolicyListener;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
-import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkIdentity;
@@ -244,6 +261,7 @@
 import com.android.internal.util.StatLogger;
 import com.android.internal.util.XmlUtils;
 import com.android.net.module.util.NetworkIdentityUtils;
+import com.android.net.module.util.PermissionUtils;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
@@ -414,6 +432,14 @@
     private static final int MSG_SET_NETWORK_TEMPLATE_ENABLED = 18;
     private static final int MSG_SUBSCRIPTION_PLANS_CHANGED = 19;
     private static final int MSG_STATS_PROVIDER_LIMIT_REACHED = 20;
+    // TODO: Add similar docs for other messages.
+    /**
+     * Message to indicate that reasons for why an uid is blocked changed.
+     * arg1 = uid
+     * arg2 = oldBlockedReasons
+     * obj = newBlockedReasons
+     */
+    private static final int MSG_BLOCKED_REASON_CHANGED = 21;
 
     private static final int UID_MSG_STATE_CHANGED = 100;
     private static final int UID_MSG_GONE = 101;
@@ -560,7 +586,10 @@
 
     /** Foreground at UID granularity. */
     @GuardedBy("mUidRulesFirstLock")
-    final SparseArray<UidState> mUidState = new SparseArray<UidState>();
+    private final SparseArray<UidState> mUidState = new SparseArray<>();
+
+    @GuardedBy("mUidRulesFirstLock")
+    private final SparseArray<UidBlockedState> mUidBlockedState = new SparseArray<>();
 
     /** Map from network ID to last observed meteredness state */
     @GuardedBy("mNetworkPoliciesSecondLock")
@@ -1189,10 +1218,11 @@
 
     private static boolean updateCapabilityChange(SparseBooleanArray lastValues, boolean newValue,
             Network network) {
-        final boolean lastValue = lastValues.get(network.netId, false);
-        final boolean changed = (lastValue != newValue) || lastValues.indexOfKey(network.netId) < 0;
+        final boolean lastValue = lastValues.get(network.getNetId(), false);
+        final boolean changed = (lastValue != newValue)
+                || lastValues.indexOfKey(network.getNetId()) < 0;
         if (changed) {
-            lastValues.put(network.netId, newValue);
+            lastValues.put(network.getNetId(), newValue);
         }
         return changed;
     }
@@ -1215,7 +1245,7 @@
                         mNetworkRoaming, newRoaming, network);
 
                 if (meteredChanged || roamingChanged) {
-                    mLogger.meterednessChanged(network.netId, newMetered);
+                    mLogger.meterednessChanged(network.getNetId(), newMetered);
                     updateNetworkRulesNL();
                 }
             }
@@ -1891,16 +1921,7 @@
      * Collect all ifaces from a {@link NetworkStateSnapshot} into the given set.
      */
     private static void collectIfaces(ArraySet<String> ifaces, NetworkStateSnapshot snapshot) {
-        final String baseIface = snapshot.linkProperties.getInterfaceName();
-        if (baseIface != null) {
-            ifaces.add(baseIface);
-        }
-        for (LinkProperties stackedLink : snapshot.linkProperties.getStackedLinks()) {
-            final String stackedIface = stackedLink.getInterfaceName();
-            if (stackedIface != null) {
-                ifaces.add(stackedIface);
-            }
-        }
+        ifaces.addAll(snapshot.linkProperties.getAllInterfaceNames());
     }
 
     /**
@@ -1981,7 +2002,7 @@
         mNetIdToSubId.clear();
         final ArrayMap<NetworkStateSnapshot, NetworkIdentity> identified = new ArrayMap<>();
         for (final NetworkStateSnapshot snapshot : snapshots) {
-            mNetIdToSubId.put(snapshot.network.netId, parseSubId(snapshot));
+            mNetIdToSubId.put(snapshot.network.getNetId(), parseSubId(snapshot));
 
             // Policies matched by NPMS only match by subscriber ID or by ssid. Thus subtype
             // in the object created here is never used and its value doesn't matter, so use
@@ -2879,15 +2900,18 @@
     }
 
     @Override
-    public void registerListener(INetworkPolicyListener listener) {
+    public void registerListener(@NonNull INetworkPolicyListener listener) {
+        Objects.requireNonNull(listener);
         // TODO: Remove CONNECTIVITY_INTERNAL and the *AnyPermissionOf methods above after all apps
         //  have declared OBSERVE_NETWORK_POLICY.
         enforceAnyPermissionOf(CONNECTIVITY_INTERNAL, OBSERVE_NETWORK_POLICY);
         mListeners.register(listener);
+        // TODO: Send callbacks to the newly registered listener
     }
 
     @Override
-    public void unregisterListener(INetworkPolicyListener listener) {
+    public void unregisterListener(@NonNull INetworkPolicyListener listener) {
+        Objects.requireNonNull(listener);
         // TODO: Remove CONNECTIVITY_INTERNAL and the *AnyPermissionOf methods above after all apps
         //  have declared OBSERVE_NETWORK_POLICY.
         enforceAnyPermissionOf(CONNECTIVITY_INTERNAL, OBSERVE_NETWORK_POLICY);
@@ -3081,8 +3105,16 @@
     @Override
     public int getRestrictBackgroundByCaller() {
         mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG);
-        final int uid = Binder.getCallingUid();
+        return getRestrictBackgroundStatusInternal(Binder.getCallingUid());
+    }
 
+    @Override
+    public int getRestrictBackgroundStatus(int uid) {
+        PermissionUtils.enforceNetworkStackPermission(mContext);
+        return getRestrictBackgroundStatusInternal(uid);
+    }
+
+    private int getRestrictBackgroundStatusInternal(int uid) {
         synchronized (mUidRulesFirstLock) {
             // Must clear identity because getUidPolicy() is restricted to system.
             final long token = Binder.clearCallingIdentity();
@@ -3551,6 +3583,7 @@
      * Get multipath preference value for the given network.
      */
     public int getMultipathPreference(Network network) {
+        PermissionUtils.enforceNetworkStackPermission(mContext);
         final Integer preference = mMultipathPolicyTracker.getMultipathPreference(network);
         if (preference != null) {
             return preference;
@@ -3923,6 +3956,7 @@
                 mUidRules.put(uid, newUidRule);
                 mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRule).sendToTarget();
             }
+            updateBlockedReasonsForRestrictedModeUL(uid);
         });
         if (mRestrictedNetworkingMode) {
             // firewall rules only need to be set when this mode is being enabled.
@@ -3943,6 +3977,7 @@
             mUidRules.put(uid, newUidRule);
             mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRule).sendToTarget();
         }
+        updateBlockedReasonsForRestrictedModeUL(uid);
 
         // if restricted networking mode is on, and the app has an access exemption, the uid rule
         // will not change, but the firewall rule will have to be updated.
@@ -3954,6 +3989,31 @@
         }
     }
 
+    private void updateBlockedReasonsForRestrictedModeUL(int uid) {
+        UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
+        if (uidBlockedState == null) {
+            uidBlockedState = new UidBlockedState();
+            mUidBlockedState.put(uid, uidBlockedState);
+        }
+        final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
+        if (mRestrictedNetworkingMode) {
+            uidBlockedState.blockedReasons |= BLOCKED_REASON_RESTRICTED_MODE;
+        } else {
+            uidBlockedState.blockedReasons &= ~BLOCKED_REASON_RESTRICTED_MODE;
+        }
+        if (hasRestrictedModeAccess(uid)) {
+            uidBlockedState.allowedReasons |= ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS;
+        } else {
+            uidBlockedState.allowedReasons &= ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS;
+        }
+        uidBlockedState.updateEffectiveBlockedReasons();
+        if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) {
+            mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid,
+                    uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons)
+                    .sendToTarget();
+        }
+    }
+
     private int getNewRestrictedModeUidRule(int uid, int oldUidRule) {
         int newRule = oldUidRule;
         newRule &= ~MASK_RESTRICTED_MODE_NETWORKS;
@@ -4074,11 +4134,21 @@
         boolean isWhitelisted = mPowerSaveTempWhitelistAppIds.get(appId)
                 || mPowerSaveWhitelistAppIds.get(appId);
         if (!deviceIdleMode) {
-            isWhitelisted = isWhitelisted || mPowerSaveWhitelistExceptIdleAppIds.get(appId);
+            isWhitelisted = isWhitelisted || isWhitelistedFromPowerSaveExceptIdleUL(uid);
         }
         return isWhitelisted;
     }
 
+    /**
+     * Returns whether a uid is allowlisted from power saving restrictions, except Device idle
+     * (eg: Battery Saver and app idle).
+     */
+    @GuardedBy("mUidRulesFirstLock")
+    private boolean isWhitelistedFromPowerSaveExceptIdleUL(int uid) {
+        final int appId = UserHandle.getAppId(uid);
+        return mPowerSaveWhitelistExceptIdleAppIds.get(appId);
+    }
+
     // NOTE: since both fw_dozable and fw_powersave uses the same map
     // (mPowerSaveTempWhitelistAppIds) for allowlisting, we can reuse their logic in this method.
     @GuardedBy("mUidRulesFirstLock")
@@ -4523,6 +4593,11 @@
         final int oldUidRules = mUidRules.get(uid, RULE_NONE);
         final boolean isForeground = isUidForegroundOnRestrictBackgroundUL(uid);
         final boolean isRestrictedByAdmin = isRestrictedByAdminUL(uid);
+        UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
+        if (uidBlockedState == null) {
+            uidBlockedState = new UidBlockedState();
+            mUidBlockedState.put(uid, uidBlockedState);
+        }
 
         final boolean isDenied = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
         final boolean isAllowed = (uidPolicy & POLICY_ALLOW_METERED_BACKGROUND) != 0;
@@ -4547,6 +4622,16 @@
             }
         }
 
+        int newBlockedReasons = BLOCKED_REASON_NONE;
+        int newAllowedReasons = ALLOWED_REASON_NONE;
+        newBlockedReasons |= (isRestrictedByAdmin ? BLOCKED_METERED_REASON_ADMIN_DISABLED : 0);
+        newBlockedReasons |= (mRestrictBackground ? BLOCKED_METERED_REASON_DATA_SAVER : 0);
+        newBlockedReasons |= (isDenied ? BLOCKED_METERED_REASON_USER_RESTRICTED : 0);
+
+        newAllowedReasons |= (isSystem(uid) ? ALLOWED_METERED_REASON_SYSTEM : 0);
+        newAllowedReasons |= (isForeground ? ALLOWED_METERED_REASON_FOREGROUND : 0);
+        newAllowedReasons |= (isAllowed ? ALLOWED_METERED_REASON_USER_EXEMPTED : 0);
+
         if (LOGV) {
             Log.v(TAG, "updateRuleForRestrictBackgroundUL(" + uid + ")"
                     + ": isForeground=" +isForeground
@@ -4619,6 +4704,18 @@
             // Dispatch changed rule to existing listeners.
             mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget();
         }
+
+        final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
+        uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons
+                & ~BLOCKED_METERED_REASON_MASK) | newBlockedReasons;
+        uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons
+                & ~ALLOWED_METERED_REASON_MASK) | newAllowedReasons;
+        uidBlockedState.updateEffectiveBlockedReasons();
+        if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) {
+            mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid,
+                    uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons)
+                    .sendToTarget();
+        }
     }
 
     /**
@@ -4692,6 +4789,12 @@
         // Copy existing uid rules and clear ALL_NETWORK rules.
         int newUidRules = oldUidRules & (~MASK_ALL_NETWORKS);
 
+        UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
+        if (uidBlockedState == null) {
+            uidBlockedState = new UidBlockedState();
+            mUidBlockedState.put(uid, uidBlockedState);
+        }
+
         // First step: define the new rule based on user restrictions and foreground state.
 
         // NOTE: if statements below could be inlined, but it's easier to understand the logic
@@ -4704,6 +4807,20 @@
             newUidRules |= isWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL;
         }
 
+        int newBlockedReasons = BLOCKED_REASON_NONE;
+        int newAllowedReasons = ALLOWED_REASON_NONE;
+        newBlockedReasons |= (mRestrictPower ? BLOCKED_REASON_BATTERY_SAVER : 0);
+        newBlockedReasons |= (mDeviceIdleMode ? BLOCKED_REASON_DOZE : 0);
+        newBlockedReasons |= (isUidIdle ? BLOCKED_REASON_APP_STANDBY : 0);
+        newBlockedReasons |= (uidBlockedState.blockedReasons & BLOCKED_REASON_RESTRICTED_MODE);
+
+        newAllowedReasons |= (isSystem(uid) ? ALLOWED_REASON_SYSTEM : 0);
+        newAllowedReasons |= (isForeground ? ALLOWED_REASON_FOREGROUND : 0);
+        newAllowedReasons |= (isWhitelistedFromPowerSaveUL(uid, true)
+                ? ALLOWED_REASON_POWER_SAVE_ALLOWLIST : 0);
+        newAllowedReasons |= (isWhitelistedFromPowerSaveExceptIdleUL(uid)
+                ? ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST : 0);
+
         if (LOGV) {
             Log.v(TAG, "updateRulesForPowerRestrictionsUL(" + uid + ")"
                     + ", isIdle: " + isUidIdle
@@ -4735,6 +4852,18 @@
             mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget();
         }
 
+        final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
+        uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons
+                & BLOCKED_METERED_REASON_MASK) | newBlockedReasons;
+        uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons
+                & ALLOWED_METERED_REASON_MASK) | newAllowedReasons;
+        uidBlockedState.updateEffectiveBlockedReasons();
+        if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) {
+            mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid,
+                    uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons)
+                    .sendToTarget();
+        }
+
         return newUidRules;
     }
 
@@ -4764,61 +4893,57 @@
     }
 
     private void dispatchUidRulesChanged(INetworkPolicyListener listener, int uid, int uidRules) {
-        if (listener != null) {
-            try {
-                listener.onUidRulesChanged(uid, uidRules);
-            } catch (RemoteException ignored) {
-            }
+        try {
+            listener.onUidRulesChanged(uid, uidRules);
+        } catch (RemoteException ignored) {
         }
     }
 
     private void dispatchMeteredIfacesChanged(INetworkPolicyListener listener,
             String[] meteredIfaces) {
-        if (listener != null) {
-            try {
-                listener.onMeteredIfacesChanged(meteredIfaces);
-            } catch (RemoteException ignored) {
-            }
+        try {
+            listener.onMeteredIfacesChanged(meteredIfaces);
+        } catch (RemoteException ignored) {
         }
     }
 
     private void dispatchRestrictBackgroundChanged(INetworkPolicyListener listener,
             boolean restrictBackground) {
-        if (listener != null) {
-            try {
-                listener.onRestrictBackgroundChanged(restrictBackground);
-            } catch (RemoteException ignored) {
-            }
+        try {
+            listener.onRestrictBackgroundChanged(restrictBackground);
+        } catch (RemoteException ignored) {
         }
     }
 
     private void dispatchUidPoliciesChanged(INetworkPolicyListener listener, int uid,
             int uidPolicies) {
-        if (listener != null) {
-            try {
-                listener.onUidPoliciesChanged(uid, uidPolicies);
-            } catch (RemoteException ignored) {
-            }
+        try {
+            listener.onUidPoliciesChanged(uid, uidPolicies);
+        } catch (RemoteException ignored) {
         }
     }
 
     private void dispatchSubscriptionOverride(INetworkPolicyListener listener, int subId,
             int overrideMask, int overrideValue, int[] networkTypes) {
-        if (listener != null) {
-            try {
-                listener.onSubscriptionOverride(subId, overrideMask, overrideValue, networkTypes);
-            } catch (RemoteException ignored) {
-            }
+        try {
+            listener.onSubscriptionOverride(subId, overrideMask, overrideValue, networkTypes);
+        } catch (RemoteException ignored) {
         }
     }
 
     private void dispatchSubscriptionPlansChanged(INetworkPolicyListener listener, int subId,
             SubscriptionPlan[] plans) {
-        if (listener != null) {
-            try {
-                listener.onSubscriptionPlansChanged(subId, plans);
-            } catch (RemoteException ignored) {
-            }
+        try {
+            listener.onSubscriptionPlansChanged(subId, plans);
+        } catch (RemoteException ignored) {
+        }
+    }
+
+    private void dispatchBlockedReasonChanged(INetworkPolicyListener listener, int uid,
+            int oldBlockedReasons, int newBlockedReasons) {
+        try {
+            listener.onBlockedReasonChanged(uid, oldBlockedReasons, newBlockedReasons);
+        } catch (RemoteException ignored) {
         }
     }
 
@@ -4975,6 +5100,19 @@
                     mListeners.finishBroadcast();
                     return true;
                 }
+                case MSG_BLOCKED_REASON_CHANGED: {
+                    final int uid = msg.arg1;
+                    final int newBlockedReasons = msg.arg2;
+                    final int oldBlockedReasons = (int) msg.obj;
+                    final int length = mListeners.beginBroadcast();
+                    for (int i = 0; i < length; i++) {
+                        final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
+                        dispatchBlockedReasonChanged(listener, uid,
+                                oldBlockedReasons, newBlockedReasons);
+                    }
+                    mListeners.finishBroadcast();
+                    return true;
+                }
                 default: {
                     return false;
                 }
@@ -5634,7 +5772,7 @@
 
     @GuardedBy("mNetworkPoliciesSecondLock")
     private int getSubIdLocked(Network network) {
-        return mNetIdToSubId.get(network.netId, INVALID_SUBSCRIPTION_ID);
+        return mNetIdToSubId.get(network.getNetId(), INVALID_SUBSCRIPTION_ID);
     }
 
     @GuardedBy("mNetworkPoliciesSecondLock")
@@ -5706,6 +5844,56 @@
         return (bundle != null) ? bundle.getBoolean(key, defaultValue) : defaultValue;
     }
 
+    private class UidBlockedState {
+        public int blockedReasons;
+        public int allowedReasons;
+        public int effectiveBlockedReasons;
+
+        UidBlockedState() {
+            blockedReasons = BLOCKED_REASON_NONE;
+            allowedReasons = ALLOWED_REASON_NONE;
+            effectiveBlockedReasons = BLOCKED_REASON_NONE;
+        }
+
+        void updateEffectiveBlockedReasons() {
+            effectiveBlockedReasons = blockedReasons;
+            // If the uid is not subject to any blocked reasons, then return early
+            if (blockedReasons == BLOCKED_REASON_NONE) {
+                return;
+            }
+            if ((allowedReasons & ALLOWED_REASON_SYSTEM) != 0) {
+                effectiveBlockedReasons = (blockedReasons & ALLOWED_METERED_REASON_MASK);
+            }
+            if ((allowedReasons & ALLOWED_METERED_REASON_SYSTEM) != 0) {
+                effectiveBlockedReasons = (blockedReasons & ~ALLOWED_METERED_REASON_MASK);
+            }
+            if ((allowedReasons & ALLOWED_REASON_FOREGROUND) != 0) {
+                effectiveBlockedReasons &= ~BLOCKED_REASON_BATTERY_SAVER;
+                effectiveBlockedReasons &= ~BLOCKED_REASON_DOZE;
+                effectiveBlockedReasons &= ~BLOCKED_REASON_APP_STANDBY;
+            }
+            if ((allowedReasons & ALLOWED_METERED_REASON_FOREGROUND) != 0) {
+                effectiveBlockedReasons &= ~BLOCKED_METERED_REASON_DATA_SAVER;
+                effectiveBlockedReasons &= ~BLOCKED_METERED_REASON_USER_RESTRICTED;
+            }
+            if ((allowedReasons & ALLOWED_REASON_POWER_SAVE_ALLOWLIST) != 0) {
+                effectiveBlockedReasons &= ~BLOCKED_REASON_BATTERY_SAVER;
+                effectiveBlockedReasons &= ~BLOCKED_REASON_DOZE;
+                effectiveBlockedReasons &= ~BLOCKED_REASON_APP_STANDBY;
+            }
+            if ((allowedReasons & ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST) != 0) {
+                effectiveBlockedReasons &= ~BLOCKED_REASON_BATTERY_SAVER;
+                effectiveBlockedReasons &= ~BLOCKED_REASON_APP_STANDBY;
+            }
+            if ((allowedReasons & ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS) != 0) {
+                effectiveBlockedReasons &= ~BLOCKED_REASON_RESTRICTED_MODE;
+            }
+            if ((allowedReasons & ALLOWED_METERED_REASON_USER_EXEMPTED) != 0) {
+                effectiveBlockedReasons &= ~BLOCKED_METERED_REASON_DATA_SAVER;
+            }
+        }
+    }
+
     private class NotificationId {
         private final String mTag;
         private final int mId;
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 7b376847..fc3174f 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -24,6 +24,7 @@
 import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.EXTRA_UID;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkIdentity.SUBTYPE_COMBINED;
 import static android.net.NetworkStack.checkNetworkStackPermission;
 import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
@@ -65,6 +66,7 @@
 import static android.provider.Settings.Global.NETSTATS_UID_TAG_DELETE_AGE;
 import static android.provider.Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES;
 import static android.provider.Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
@@ -92,7 +94,6 @@
 import android.net.INetworkManagementEventObserver;
 import android.net.INetworkStatsService;
 import android.net.INetworkStatsSession;
-import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkIdentity;
@@ -101,7 +102,9 @@
 import android.net.NetworkStats;
 import android.net.NetworkStats.NonMonotonicObserver;
 import android.net.NetworkStatsHistory;
+import android.net.NetworkSpecifier;
 import android.net.NetworkTemplate;
+import android.net.TelephonyNetworkSpecifier;
 import android.net.TrafficStats;
 import android.net.UnderlyingNetworkInfo;
 import android.net.Uri;
@@ -131,6 +134,7 @@
 import android.service.NetworkStatsServiceDumpProto;
 import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionPlan;
+import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -1320,8 +1324,9 @@
                             ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(),
                             ident.getRoaming(), true /* metered */,
                             true /* onDefaultNetwork */, ident.getOemManaged());
-                    findOrCreateNetworkIdentitySet(mActiveIfaces, IFACE_VT).add(vtIdent);
-                    findOrCreateNetworkIdentitySet(mActiveUidIfaces, IFACE_VT).add(vtIdent);
+                    final String ifaceVt = IFACE_VT + getSubIdForMobile(snapshot);
+                    findOrCreateNetworkIdentitySet(mActiveIfaces, ifaceVt).add(vtIdent);
+                    findOrCreateNetworkIdentitySet(mActiveUidIfaces, ifaceVt).add(vtIdent);
                 }
 
                 if (isMobile) {
@@ -1358,17 +1363,18 @@
             // (or non eBPF offloaded) TX they would appear on both, however egress interface
             // accounting is explicitly bypassed for traffic from the clat uid.
             //
-            final List<LinkProperties> stackedLinks = snapshot.linkProperties.getStackedLinks();
-            for (LinkProperties stackedLink : stackedLinks) {
-                final String stackedIface = stackedLink.getInterfaceName();
-                if (stackedIface != null) {
-                    findOrCreateNetworkIdentitySet(mActiveIfaces, stackedIface).add(ident);
-                    findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident);
+            // TODO: This code might be combined to above code.
+            for (String iface : snapshot.linkProperties.getAllInterfaceNames()) {
+                // baseIface has been handled, so ignore it.
+                if (TextUtils.equals(baseIface, iface)) continue;
+                if (iface != null) {
+                    findOrCreateNetworkIdentitySet(mActiveIfaces, iface).add(ident);
+                    findOrCreateNetworkIdentitySet(mActiveUidIfaces, iface).add(ident);
                     if (isMobile) {
-                        mobileIfaces.add(stackedIface);
+                        mobileIfaces.add(iface);
                     }
 
-                    mStatsFactory.noteStackedIface(stackedIface, baseIface);
+                    mStatsFactory.noteStackedIface(iface, baseIface);
                 }
             }
         }
@@ -1376,6 +1382,20 @@
         mMobileIfaces = mobileIfaces.toArray(new String[mobileIfaces.size()]);
     }
 
+    private static int getSubIdForMobile(@NonNull NetworkStateSnapshot state) {
+        if (!state.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
+            throw new IllegalArgumentException("Mobile state need capability TRANSPORT_CELLULAR");
+        }
+
+        final NetworkSpecifier spec = state.networkCapabilities.getNetworkSpecifier();
+        if (spec instanceof TelephonyNetworkSpecifier) {
+             return ((TelephonyNetworkSpecifier) spec).getSubscriptionId();
+        } else {
+            Slog.wtf(TAG, "getSubIdForState invalid NetworkSpecifier");
+            return INVALID_SUBSCRIPTION_ID;
+        }
+    }
+
     /**
      * For networks with {@code TRANSPORT_CELLULAR}, get subType that was obtained through
      * {@link PhoneStateListener}. Otherwise, return 0 given that other networks with different
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 7d1827d..0c1ba96 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -32,6 +32,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -177,6 +178,8 @@
 
     private Map<String, List<String>> mOemLockedApps = new HashMap();
 
+    private int mCurrentUserId = UserHandle.USER_NULL;
+
     public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
             ZenModeHelper zenHelper, NotificationChannelLogger notificationChannelLogger,
             AppOpsManager appOpsManager,
@@ -191,7 +194,8 @@
 
         updateBadgingEnabled();
         updateBubblesEnabled();
-        syncChannelsBypassingDnd(mContext.getUserId());
+        mCurrentUserId = ActivityManager.getCurrentUser();
+        syncChannelsBypassingDnd();
     }
 
     public void readXml(XmlPullParser parser, boolean forRestore, int userId)
@@ -790,7 +794,7 @@
                     // but the system can
                     if (group.isBlocked() != oldGroup.isBlocked()) {
                         group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE);
-                        updateChannelsBypassingDnd(mContext.getUserId());
+                        updateChannelsBypassingDnd();
                     }
                 }
             }
@@ -871,13 +875,13 @@
                 // fields on the channel yet
                 if (existing.getUserLockedFields() == 0 && hasDndAccess) {
                     boolean bypassDnd = channel.canBypassDnd();
-                    if (bypassDnd != existing.canBypassDnd()) {
+                    if (bypassDnd != existing.canBypassDnd() || wasUndeleted) {
                         existing.setBypassDnd(bypassDnd);
                         needsPolicyFileChange = true;
 
                         if (bypassDnd != mAreChannelsBypassingDnd
                                 || previousExistingImportance != existing.getImportance()) {
-                            updateChannelsBypassingDnd(mContext.getUserId());
+                            updateChannelsBypassingDnd();
                         }
                     }
                 }
@@ -941,7 +945,7 @@
 
             r.channels.put(channel.getId(), channel);
             if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
-                updateChannelsBypassingDnd(mContext.getUserId());
+                updateChannelsBypassingDnd();
             }
             MetricsLogger.action(getChannelLog(channel, pkg).setType(
                     com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
@@ -1013,7 +1017,7 @@
 
             if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd
                     || channel.getImportance() != updatedChannel.getImportance()) {
-                updateChannelsBypassingDnd(mContext.getUserId());
+                updateChannelsBypassingDnd();
             }
         }
         updateConfig();
@@ -1110,7 +1114,7 @@
             mNotificationChannelLogger.logNotificationChannelDeleted(channel, uid, pkg);
 
             if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
-                updateChannelsBypassingDnd(mContext.getUserId());
+                updateChannelsBypassingDnd();
             }
         }
     }
@@ -1454,7 +1458,7 @@
                 }
             }
             if (!deletedChannelIds.isEmpty() && mAreChannelsBypassingDnd) {
-                updateChannelsBypassingDnd(mContext.getUserId());
+                updateChannelsBypassingDnd();
             }
             return deletedChannelIds;
         }
@@ -1600,29 +1604,29 @@
     }
 
     /**
-     * Syncs {@link #mAreChannelsBypassingDnd} with the user's notification policy before
+     * Syncs {@link #mAreChannelsBypassingDnd} with the current user's notification policy before
      * updating
-     * @param userId
      */
-    private void syncChannelsBypassingDnd(int userId) {
+    private void syncChannelsBypassingDnd() {
         mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state
                 & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
-        updateChannelsBypassingDnd(userId);
+        updateChannelsBypassingDnd();
     }
 
     /**
-     * Updates the user's NotificationPolicy based on whether the given userId
+     * Updates the user's NotificationPolicy based on whether the current userId
      * has channels bypassing DND
      * @param userId
      */
-    private void updateChannelsBypassingDnd(int userId) {
+    private void updateChannelsBypassingDnd() {
         synchronized (mPackagePreferences) {
             final int numPackagePreferences = mPackagePreferences.size();
             for (int i = 0; i < numPackagePreferences; i++) {
                 final PackagePreferences r = mPackagePreferences.valueAt(i);
-                // Package isn't associated with this userId or notifications from this package are
-                // blocked
-                if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
+                // Package isn't associated with the current userId or notifications from this
+                // package are blocked
+                if (mCurrentUserId != UserHandle.getUserId(r.uid)
+                        || r.importance == IMPORTANCE_NONE) {
                     continue;
                 }
 
@@ -2168,14 +2172,16 @@
      * Called when user switches
      */
     public void onUserSwitched(int userId) {
-        syncChannelsBypassingDnd(userId);
+        mCurrentUserId = userId;
+        syncChannelsBypassingDnd();
     }
 
     /**
      * Called when user is unlocked
      */
     public void onUserUnlocked(int userId) {
-        syncChannelsBypassingDnd(userId);
+        mCurrentUserId = userId;
+        syncChannelsBypassingDnd();
     }
 
     public void onUserRemoved(int userId) {
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
index 9c4c510..cc6a824 100644
--- a/services/core/java/com/android/server/os/NativeTombstoneManager.java
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -411,8 +411,13 @@
                             processName = stream.readString(Tombstone.PROCESS_NAME);
                             break;
 
-                        case (int) Tombstone.CAUSE:
-                            long token = stream.start(Tombstone.CAUSE);
+                        case (int) Tombstone.CAUSES:
+                            if (!crashReason.equals("")) {
+                                // Causes appear in decreasing order of likelihood. For now we only
+                                // want the most likely crash reason here, so ignore all others.
+                                break;
+                            }
+                            long token = stream.start(Tombstone.CAUSES);
                         cause:
                             while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
                                 switch (stream.getFieldNumber()) {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index b39a6b4..6ad43ce 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -51,6 +51,7 @@
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.os.storage.StorageManager;
@@ -62,6 +63,8 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.dex.ArtManagerService;
+import com.android.server.pm.dex.ArtStatsLogUtils;
+import com.android.server.pm.dex.ArtStatsLogUtils.ArtStatsLogger;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DexoptOptions;
 import com.android.server.pm.dex.DexoptUtils;
@@ -99,6 +102,8 @@
     private final PowerManager.WakeLock mDexoptWakeLock;
     private volatile boolean mSystemReady;
 
+    private final ArtStatsLogger mArtStatsLogger = new ArtStatsLogger();
+
     PackageDexOptimizer(Installer installer, Object installLock, Context context,
             String wakeLockTag) {
         this.mInstaller = installer;
@@ -252,6 +257,29 @@
                         profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid,
                         packageStats, options.isDowngrade(), profileName, dexMetadataPath,
                         options.getCompilationReason());
+
+                // Only report metrics for base apk for now.
+                // TODO: add ISA and APK type to metrics.
+                // OTAPreopt doesn't have stats so don't report in that case.
+                if (pkg.getBaseCodePath().equals(path) && packageStats != null) {
+                    Trace.traceBegin(Trace.TRACE_TAG_PACKAGE_MANAGER, "dex2oat-metrics");
+                    try {
+                        long sessionId = Math.randomLongInternal();
+                        ArtStatsLogUtils.writeStatsLog(
+                                mArtStatsLogger,
+                                sessionId,
+                                path,
+                                compilerFilter,
+                                sharedGid,
+                                packageStats.getCompileTime(path),
+                                dexMetadataPath,
+                                options.getCompilationReason(),
+                                newResult);
+                    } finally {
+                        Trace.traceEnd(Trace.TRACE_TAG_PACKAGE_MANAGER);
+                    }
+                }
+
                 // The end result is:
                 //  - FAILED if any path failed,
                 //  - PERFORMED if at least one path needed compilation,
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 06b54b5..4038bf2 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -203,7 +203,7 @@
             newSigningDetails = ApkSignatureVerifier.verify(apexPath, minSignatureScheme);
         } catch (PackageParserException e) {
             throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
-                    "Failed to parse APEX package " + apexPath, e);
+                    "Failed to parse APEX package " + apexPath + " : " + e, e);
         }
 
         // Get signing details of the existing package
@@ -221,7 +221,8 @@
                 existingApexPkg.applicationInfo.sourceDir, SignatureSchemeVersion.JAR);
         } catch (PackageParserException e) {
             throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
-                    "Failed to parse APEX package " + existingApexPkg.applicationInfo.sourceDir, e);
+                    "Failed to parse APEX package " + existingApexPkg.applicationInfo.sourceDir
+                            + " : " + e, e);
         }
 
         // Verify signing details for upgrade
@@ -283,7 +284,7 @@
                 }
             } catch (PackageParserException e) {
                 throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
-                        "Failed to parse APEX package " + apexInfo.modulePath, e);
+                        "Failed to parse APEX package " + apexInfo.modulePath + " : " + e, e);
             }
             final PackageInfo activePackage = mApexManager.getPackageInfo(packageInfo.packageName,
                     ApexManager.MATCH_ACTIVE_PACKAGE);
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
new file mode 100644
index 0000000..c8dc1b1
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -0,0 +1,226 @@
+/*
+ * 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.pm.dex;
+
+import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED;
+import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY;
+import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED;
+import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK;
+import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK;
+
+import android.util.jar.StrictJarFile;
+import android.util.Slog;
+
+import com.android.internal.art.ArtStatsLog;
+import com.android.server.pm.PackageManagerService;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+
+/** Utils class to report ART metrics to statsd. */
+public class ArtStatsLogUtils {
+    private static final String TAG = ArtStatsLogUtils.class.getSimpleName();
+    private static final String PROFILE_DEX_METADATA = "primary.prof";
+    private static final String VDEX_DEX_METADATA = "primary.vdex";
+
+
+    private static final int ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY =
+            ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY;
+    private static final int ART_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED =
+            ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED;
+    private static final int ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED =
+            ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED;
+
+    private static final int ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK =
+            ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK;
+    private static final int ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK =
+            ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK;
+
+    private static final Map<Integer, Integer> COMPILATION_REASON_MAP = new HashMap();
+
+    static {
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_UNKNOWN, ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_UNKNOWN);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_FIRST_BOOT, ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_FIRST_BOOT);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_BOOT_AFTER_OTA, ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_BOOT);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_POST_BOOT, ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_POST_BOOT);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL, ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_FAST, ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_FAST);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_BULK, ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_BULK_SECONDARY,
+                ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_BULK_DOWNGRADED,
+                ART_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED,
+                ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_BACKGROUND_DEXOPT, ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_BG_DEXOPT);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_AB_OTA, ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_AB_OTA);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE,
+                ArtStatsLog.
+                        ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INACTIVE);
+        COMPILATION_REASON_MAP.put(PackageManagerService.REASON_SHARED,
+                ArtStatsLog.ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_SHARED);
+    }
+
+    private static final Map<String, Integer> COMPILE_FILTER_MAP = new HashMap();
+
+    static {
+        COMPILE_FILTER_MAP.put("error", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_ERROR);
+        COMPILE_FILTER_MAP.put("unknown", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_UNKNOWN);
+        COMPILE_FILTER_MAP.put("assume-verified", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_ASSUMED_VERIFIED);
+        COMPILE_FILTER_MAP.put("extract", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EXTRACT);
+        COMPILE_FILTER_MAP.put("verify", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_VERIFY);
+        COMPILE_FILTER_MAP.put("quicken", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_QUICKEN);
+        COMPILE_FILTER_MAP.put("space-profile", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPACE_PROFILE);
+        COMPILE_FILTER_MAP.put("space", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPACE);
+        COMPILE_FILTER_MAP.put("speed-profile", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPEED_PROFILE);
+        COMPILE_FILTER_MAP.put("speed", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPEED);
+        COMPILE_FILTER_MAP.put("everything-profile", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EVERYTHING_PROFILE);
+        COMPILE_FILTER_MAP.put("everything", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EVERYTHING);
+        COMPILE_FILTER_MAP.put("run-from-apk", ArtStatsLog.
+                ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK);
+        COMPILE_FILTER_MAP.put("run-from-apk-fallback",
+                ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK);
+        COMPILE_FILTER_MAP.put("run-from-vdex-fallback",
+                ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK);
+    }
+
+    public static void writeStatsLog(
+            ArtStatsLogger logger,
+            long sessionId,
+            String path,
+            String compilerFilter,
+            int uid,
+            long compileTime,
+            String dexMetadataPath,
+            int compilationReason,
+            int result) {
+        int dexMetadataType = getDexMetadataType(dexMetadataPath);
+        logger.write(
+                sessionId,
+                uid,
+                compilationReason,
+                compilerFilter,
+                ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_RESULT_CODE,
+                result,
+                dexMetadataType);
+        logger.write(
+                sessionId,
+                uid,
+                compilationReason,
+                compilerFilter,
+                ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME,
+                compileTime,
+                dexMetadataType);
+    }
+
+    private static int getDexMetadataType(String dexMetadataPath) {
+        if (dexMetadataPath == null) {
+            return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_NONE;
+        }
+        StrictJarFile jarFile = null;
+        try {
+            jarFile = new StrictJarFile(dexMetadataPath,
+                    /*verify=*/ false,
+                    /*signatureSchemeRollbackProtectionsEnforced=*/false);
+            boolean hasProfile = findFileName(jarFile, PROFILE_DEX_METADATA);
+            boolean hasVdex = findFileName(jarFile, VDEX_DEX_METADATA);
+            if (hasProfile && hasVdex) {
+                return ArtStatsLog.
+                    ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE_AND_VDEX;
+            } else if (hasProfile) {
+                return ArtStatsLog.
+                    ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE;
+            } else if (hasVdex) {
+                return ArtStatsLog.
+                    ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_VDEX;
+            } else {
+                return ArtStatsLog.
+                    ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_UNKNOWN;
+            }
+        } catch (IOException ignore) {
+            Slog.e(TAG, "Error when parsing dex metadata " + dexMetadataPath);
+            return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_ERROR;
+        } finally {
+            try {
+                if (jarFile != null) {
+                    jarFile.close();
+                }
+            } catch (IOException ignore) {
+            }
+        }
+    }
+
+    private static boolean findFileName(StrictJarFile jarFile, String filename) throws IOException {
+        Iterator<ZipEntry> it = jarFile.iterator();
+        while (it.hasNext()) {
+            ZipEntry entry = it.next();
+            if (entry.getName().equals(filename)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static class ArtStatsLogger {
+        public void write(
+                long sessionId,
+                int uid,
+                int compilationReason,
+                String compilerFilter,
+                int kind,
+                long value,
+                int dexMetadataType) {
+            ArtStatsLog.write(
+                    ArtStatsLog.ART_DATUM_REPORTED,
+                    sessionId,
+                    uid,
+                    COMPILE_FILTER_MAP.getOrDefault(compilerFilter, ArtStatsLog.
+                            ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_UNKNOWN),
+                    COMPILATION_REASON_MAP.getOrDefault(compilationReason, ArtStatsLog.
+                            ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_UNKNOWN),
+                    /*timestamp_millis=*/ 0L,
+                    ArtStatsLog.ART_DATUM_REPORTED__THREAD_TYPE__ART_THREAD_MAIN,
+                    kind,
+                    value,
+                    dexMetadataType);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 349561d..37f3175 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -986,7 +986,7 @@
      * Fetches the battery manager object and caches it if it hasn't been fetched already.
      */
     private BatteryManager getBatteryManager() {
-        if (mBatteryManager == null) {
+        if (mBatteryManager == null && mContext != null) {
             mBatteryManager = mContext.getSystemService(BatteryManager.class);
         }
 
@@ -1008,10 +1008,6 @@
                     && mPowerManager.getCurrentThermalStatus()
                         >= PowerManager.THERMAL_STATUS_SEVERE);
 
-        if (DEBUG) {
-            Log.d(TAG, "Battery, thermal, or memory are critical: " + isBtmCritical);
-        }
-
         return isBtmCritical;
     }
 
diff --git a/services/core/java/com/android/server/policy/LegacyGlobalActions.java b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
index 9c3a394..5b48abb 100644
--- a/services/core/java/com/android/server/policy/LegacyGlobalActions.java
+++ b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
@@ -24,11 +24,11 @@
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
-import android.net.ConnectivityManager;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
@@ -113,7 +113,7 @@
     private boolean mDeviceProvisioned = false;
     private ToggleAction.State mAirplaneState = ToggleAction.State.Off;
     private boolean mIsWaitingForEcmExit = false;
-    private boolean mHasTelephony;
+    private final boolean mHasTelephony;
     private boolean mHasVibrator;
     private final boolean mShowSilentToggle;
     private final EmergencyAffordanceManager mEmergencyAffordanceManager;
@@ -137,9 +137,8 @@
         filter.addAction(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
         context.registerReceiver(mBroadcastReceiver, filter);
 
-        ConnectivityManager cm = (ConnectivityManager)
-                context.getSystemService(Context.CONNECTIVITY_SERVICE);
-        mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+        mHasTelephony =
+                context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
 
         // get notified of phone state changes
         TelephonyManager telephonyManager =
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
index fd2d8e1..fe21201 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
@@ -16,14 +16,18 @@
 
 package com.android.server.recoverysystem;
 
+import static android.os.UserHandle.USER_SYSTEM;
+
 import android.annotation.IntDef;
 import android.content.Context;
 import android.content.IntentSender;
+import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.hardware.boot.V1_0.IBootControl;
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
 import android.os.Binder;
+import android.os.Environment;
 import android.os.IRecoverySystem;
 import android.os.IRecoverySystemProgressListener;
 import android.os.PowerManager;
@@ -33,12 +37,14 @@
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
 import android.os.SystemProperties;
+import android.provider.DeviceConfig;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.widget.LockSettingsInternal;
 import com.android.internal.widget.RebootEscrowListener;
 import com.android.server.LocalServices;
@@ -48,10 +54,13 @@
 
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
+import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * The recovery system service is responsible for coordinating recovery related
@@ -81,6 +90,12 @@
 
     private static final int SOCKET_CONNECTION_MAX_RETRY = 30;
 
+    static final String REQUEST_LSKF_TIMESTAMP_PREF_SUFFIX = "_request_lskf_timestamp";
+    static final String REQUEST_LSKF_COUNT_PREF_SUFFIX = "_request_lskf_count";
+
+    static final String LSKF_CAPTURED_TIMESTAMP_PREF = "lskf_captured_timestamp";
+    static final String LSKF_CAPTURED_COUNT_PREF = "lskf_captured_count";
+
     private final Injector mInjector;
     private final Context mContext;
 
@@ -121,22 +136,93 @@
      */
     @IntDef({ ROR_NEED_PREPARATION,
             ROR_SKIP_PREPARATION_AND_NOTIFY,
-            ROR_SKIP_PREPARATION_NOT_NOTIFY })
+            ROR_SKIP_PREPARATION_NOT_NOTIFY})
     private @interface ResumeOnRebootActionsOnRequest {}
 
     /**
      * The action to perform upon resume on reboot clear request for a given client.
      */
-    @IntDef({ROR_NOT_REQUESTED,
+    @IntDef({ ROR_NOT_REQUESTED,
             ROR_REQUESTED_NEED_CLEAR,
             ROR_REQUESTED_SKIP_CLEAR})
-    private @interface ResumeOnRebootActionsOnClear{}
+    private @interface ResumeOnRebootActionsOnClear {}
+
+    /**
+     * The error codes for reboots initiated by resume on reboot clients.
+     */
+    private static final int REBOOT_ERROR_NONE = 0;
+    private static final int REBOOT_ERROR_UNKNOWN = 1;
+    private static final int REBOOT_ERROR_INVALID_PACKAGE_NAME = 2;
+    private static final int REBOOT_ERROR_LSKF_NOT_CAPTURED = 3;
+    private static final int REBOOT_ERROR_SLOT_MISMATCH = 4;
+    private static final int REBOOT_ERROR_ARM_REBOOT_ESCROW_FAILURE = 5;
+
+    @IntDef({ REBOOT_ERROR_NONE,
+            REBOOT_ERROR_UNKNOWN,
+            REBOOT_ERROR_INVALID_PACKAGE_NAME,
+            REBOOT_ERROR_LSKF_NOT_CAPTURED,
+            REBOOT_ERROR_SLOT_MISMATCH,
+            REBOOT_ERROR_ARM_REBOOT_ESCROW_FAILURE})
+    private @interface ResumeOnRebootRebootErrorCode {}
+
+    /**
+     * Manages shared preference, i.e. the storage used for metrics reporting.
+     */
+    public static class PreferencesManager {
+        private static final String METRICS_DIR = "recovery_system";
+        private static final String METRICS_PREFS_FILE = "RecoverySystemMetricsPrefs.xml";
+
+        protected final SharedPreferences mSharedPreferences;
+        private final File mMetricsPrefsFile;
+
+        PreferencesManager(Context context) {
+            File prefsDir = new File(Environment.getDataSystemCeDirectory(USER_SYSTEM),
+                    METRICS_DIR);
+            mMetricsPrefsFile = new File(prefsDir, METRICS_PREFS_FILE);
+            mSharedPreferences = context.getSharedPreferences(mMetricsPrefsFile, 0);
+        }
+
+        /** Reads the value of a given key with type long. **/
+        public long getLong(String key, long defaultValue) {
+            return mSharedPreferences.getLong(key, defaultValue);
+        }
+
+        /** Reads the value of a given key with type int. **/
+        public int getInt(String key, int defaultValue) {
+            return mSharedPreferences.getInt(key, defaultValue);
+        }
+
+        /** Stores the value of a given key with type long. **/
+        public void putLong(String key, long value) {
+            mSharedPreferences.edit().putLong(key, value).commit();
+        }
+
+        /** Stores the value of a given key with type int. **/
+        public void putInt(String key, int value) {
+            mSharedPreferences.edit().putInt(key, value).commit();
+        }
+
+        /** Increments the value of a given key with type int. **/
+        public synchronized void incrementIntKey(String key, int defaultInitialValue) {
+            int oldValue = getInt(key, defaultInitialValue);
+            putInt(key, oldValue + 1);
+        }
+
+        /** Delete the preference file and cleanup all metrics storage. **/
+        public void deletePrefsFile() {
+            if (!mMetricsPrefsFile.delete()) {
+                Slog.w(TAG, "Failed to delete metrics prefs");
+            }
+        }
+    }
 
     static class Injector {
         protected final Context mContext;
+        protected final PreferencesManager mPrefs;
 
         Injector(Context context) {
             mContext = context;
+            mPrefs = new PreferencesManager(context);
         }
 
         public Context getContext() {
@@ -202,6 +288,43 @@
         public void threadSleep(long millis) throws InterruptedException {
             Thread.sleep(millis);
         }
+
+        public int getUidFromPackageName(String packageName) {
+            try {
+                return mContext.getPackageManager().getPackageUidAsUser(packageName, USER_SYSTEM);
+            } catch (PackageManager.NameNotFoundException e) {
+                Slog.w(TAG, "Failed to find uid for " + packageName);
+            }
+            return -1;
+        }
+
+        public PreferencesManager getMetricsPrefs() {
+            return mPrefs;
+        }
+
+        public long getCurrentTimeMillis() {
+            return System.currentTimeMillis();
+        }
+
+        public void reportRebootEscrowPreparationMetrics(int uid,
+                @ResumeOnRebootActionsOnRequest int requestResult, int requestedClientCount) {
+            FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_PREPARATION_REPORTED, uid,
+                    requestResult, requestedClientCount);
+        }
+
+        public void reportRebootEscrowLskfCapturedMetrics(int uid, int requestedClientCount,
+                int requestedToLskfCapturedDurationInSeconds) {
+            FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_LSKF_CAPTURE_REPORTED, uid,
+                    requestedClientCount, requestedToLskfCapturedDurationInSeconds);
+        }
+
+        public void reportRebootEscrowRebootMetrics(int errorCode, int uid,
+                int preparedClientCount, int requestCount, boolean slotSwitch, boolean serverBased,
+                int lskfCapturedToRebootDurationInSeconds, int lskfCapturedCounts) {
+            FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_REBOOT_REPORTED, errorCode,
+                    uid, preparedClientCount, requestCount, slotSwitch, serverBased,
+                    lskfCapturedToRebootDurationInSeconds, lskfCapturedCounts);
+        }
     }
 
     /**
@@ -361,12 +484,28 @@
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.RECOVERY)
                 != PackageManager.PERMISSION_GRANTED
                 && mContext.checkCallingOrSelfPermission(android.Manifest.permission.REBOOT)
-                        != PackageManager.PERMISSION_GRANTED) {
+                    != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Caller must have " + android.Manifest.permission.RECOVERY
                     + " or " + android.Manifest.permission.REBOOT + " for resume on reboot.");
         }
     }
 
+    private void reportMetricsOnRequestLskf(String packageName, int requestResult) {
+        int uid = mInjector.getUidFromPackageName(packageName);
+        int pendingRequestCount;
+        synchronized (this) {
+            pendingRequestCount = mCallerPendingRequest.size();
+        }
+
+        // Save the timestamp and request count for new ror request
+        PreferencesManager prefs = mInjector.getMetricsPrefs();
+        prefs.putLong(packageName + REQUEST_LSKF_TIMESTAMP_PREF_SUFFIX,
+                mInjector.getCurrentTimeMillis());
+        prefs.incrementIntKey(packageName + REQUEST_LSKF_COUNT_PREF_SUFFIX, 0);
+
+        mInjector.reportRebootEscrowPreparationMetrics(uid, requestResult, pendingRequestCount);
+    }
+
     @Override // Binder call
     public boolean requestLskf(String packageName, IntentSender intentSender) {
         enforcePermissionForResumeOnReboot();
@@ -378,6 +517,8 @@
 
         @ResumeOnRebootActionsOnRequest int action = updateRoRPreparationStateOnNewRequest(
                 packageName, intentSender);
+        reportMetricsOnRequestLskf(packageName, action);
+
         switch (action) {
             case ROR_SKIP_PREPARATION_AND_NOTIFY:
                 // We consider the preparation done if someone else has prepared.
@@ -420,12 +561,42 @@
         return needPreparation ? ROR_NEED_PREPARATION : ROR_SKIP_PREPARATION_NOT_NOTIFY;
     }
 
+    private void reportMetricsOnPreparedForReboot() {
+        long currentTimestamp = mInjector.getCurrentTimeMillis();
+
+        List<String> preparedClients;
+        synchronized (this) {
+            preparedClients = new ArrayList<>(mCallerPreparedForReboot);
+        }
+
+        // Save the timestamp & lskf capture count for lskf capture
+        PreferencesManager prefs = mInjector.getMetricsPrefs();
+        prefs.putLong(LSKF_CAPTURED_TIMESTAMP_PREF, currentTimestamp);
+        prefs.incrementIntKey(LSKF_CAPTURED_COUNT_PREF, 0);
+
+        for (String packageName : preparedClients) {
+            int uid = mInjector.getUidFromPackageName(packageName);
+
+            int durationSeconds = -1;
+            long requestLskfTimestamp = prefs.getLong(
+                    packageName + REQUEST_LSKF_TIMESTAMP_PREF_SUFFIX, -1);
+            if (requestLskfTimestamp != -1 && currentTimestamp > requestLskfTimestamp) {
+                durationSeconds = (int) (currentTimestamp - requestLskfTimestamp) / 1000;
+            }
+            Slog.i(TAG, String.format("Reporting lskf captured, lskf capture takes %d seconds for"
+                    + " package %s", durationSeconds, packageName));
+            mInjector.reportRebootEscrowLskfCapturedMetrics(uid, preparedClients.size(),
+                    durationSeconds);
+        }
+    }
+
     @Override
     public void onPreparedForReboot(boolean ready) {
         if (!ready) {
             return;
         }
         updateRoRPreparationStateOnPreparedForReboot();
+        reportMetricsOnPreparedForReboot();
     }
 
     private synchronized void updateRoRPreparationStateOnPreparedForReboot() {
@@ -462,6 +633,7 @@
             Slog.w(TAG, "Missing packageName when clearing lskf.");
             return false;
         }
+        // TODO(179105110) Clear the RoR metrics for the given packageName.
 
         @ResumeOnRebootActionsOnClear int action = updateRoRPreparationStateOnClear(packageName);
         switch (action) {
@@ -548,24 +720,84 @@
         return true;
     }
 
-    private boolean rebootWithLskfImpl(String packageName, String reason, boolean slotSwitch) {
+    private @ResumeOnRebootRebootErrorCode int armRebootEscrow(String packageName,
+            boolean slotSwitch) {
         if (packageName == null) {
             Slog.w(TAG, "Missing packageName when rebooting with lskf.");
-            return false;
+            return REBOOT_ERROR_INVALID_PACKAGE_NAME;
         }
         if (!isLskfCaptured(packageName)) {
-            return false;
+            return REBOOT_ERROR_LSKF_NOT_CAPTURED;
         }
 
         if (!verifySlotForNextBoot(slotSwitch)) {
+            return REBOOT_ERROR_SLOT_MISMATCH;
+        }
+
+        final long origId = Binder.clearCallingIdentity();
+        boolean result;
+        try {
+            result = mInjector.getLockSettingsService().armRebootEscrow();
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+
+        if (!result) {
+            Slog.w(TAG, "Failure to escrow key for reboot");
+            return REBOOT_ERROR_ARM_REBOOT_ESCROW_FAILURE;
+        }
+
+        return REBOOT_ERROR_NONE;
+    }
+
+    private boolean useServerBasedRoR() {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
+                    "server_based_ror_enabled", false);
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    private void reportMetricsOnRebootWithLskf(String packageName, boolean slotSwitch,
+            @ResumeOnRebootRebootErrorCode int errorCode) {
+        int uid = mInjector.getUidFromPackageName(packageName);
+        boolean serverBased = useServerBasedRoR();
+        int preparedClientCount;
+        synchronized (this) {
+            preparedClientCount = mCallerPreparedForReboot.size();
+        }
+
+        long currentTimestamp = mInjector.getCurrentTimeMillis();
+        int durationSeconds = -1;
+        PreferencesManager prefs = mInjector.getMetricsPrefs();
+        long lskfCapturedTimestamp = prefs.getLong(LSKF_CAPTURED_TIMESTAMP_PREF, -1);
+        if (lskfCapturedTimestamp != -1 && currentTimestamp > lskfCapturedTimestamp) {
+            durationSeconds = (int) (currentTimestamp - lskfCapturedTimestamp) / 1000;
+        }
+
+        int requestCount = prefs.getInt(packageName + REQUEST_LSKF_COUNT_PREF_SUFFIX, -1);
+        int lskfCapturedCount = prefs.getInt(LSKF_CAPTURED_COUNT_PREF, -1);
+
+        Slog.i(TAG, String.format("Reporting reboot with lskf, package name %s, client count %d,"
+                        + " request count %d, lskf captured count %d, duration since lskf captured"
+                        + " %d seconds.", packageName, preparedClientCount, requestCount,
+                lskfCapturedCount, durationSeconds));
+        mInjector.reportRebootEscrowRebootMetrics(errorCode, uid, preparedClientCount,
+                requestCount, slotSwitch, serverBased, durationSeconds, lskfCapturedCount);
+    }
+
+    private boolean rebootWithLskfImpl(String packageName, String reason, boolean slotSwitch) {
+        @ResumeOnRebootRebootErrorCode int errorCode = armRebootEscrow(packageName, slotSwitch);
+        reportMetricsOnRebootWithLskf(packageName, slotSwitch, errorCode);
+
+        if (errorCode != REBOOT_ERROR_NONE) {
             return false;
         }
 
-        // TODO(xunchang) write the vbmeta digest along with the escrowKey before reboot.
-        if (!mInjector.getLockSettingsService().armRebootEscrow()) {
-            Slog.w(TAG, "Failure to escrow key for reboot");
-            return false;
-        }
+        // Clear the metrics prefs after a successful RoR reboot.
+        mInjector.getMetricsPrefs().deletePrefsFile();
 
         PowerManager pm = mInjector.getPowerManager();
         pm.reboot(reason);
diff --git a/services/core/java/com/android/server/stats/OWNERS b/services/core/java/com/android/server/stats/OWNERS
index fc7fd22..174ad3a 100644
--- a/services/core/java/com/android/server/stats/OWNERS
+++ b/services/core/java/com/android/server/stats/OWNERS
@@ -1,7 +1,10 @@
 jeffreyhuang@google.com
 joeo@google.com
+jtnguyen@google.com
 muhammadq@google.com
+rslawik@google.com
 ruchirr@google.com
+sharaienko@google.com
 singhtejinder@google.com
 tsaichristine@google.com
 yaochen@google.com
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
index 3519465..85d2d39 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
@@ -20,7 +20,7 @@
 import android.app.AlarmManager;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.net.ConnectivityManager;
+import android.content.pm.PackageManager;
 import android.os.SystemProperties;
 import android.provider.Settings;
 
@@ -52,9 +52,7 @@
     }
 
     private boolean deviceHasTelephonyNetwork() {
-        // TODO b/150583524 Avoid the use of a deprecated API.
-        return mContext.getSystemService(ConnectivityManager.class)
-                .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index d858ae4..61bd5df 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -2934,7 +2934,11 @@
                     values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
                             sessionToken.toString());
 
-                    mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
+                    try{
+                        mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
+                    }catch(IllegalArgumentException ex){
+                        Slog.w(TAG, "error in insert db for MSG_LOG_WATCH_START", ex);
+                    }
                     args.recycle();
                     break;
                 }
@@ -2949,7 +2953,11 @@
                     values.put(TvContract.WatchedPrograms.COLUMN_INTERNAL_SESSION_TOKEN,
                             sessionToken.toString());
 
-                    mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
+                    try{
+                        mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
+                    }catch(IllegalArgumentException ex){
+                        Slog.w(TAG, "error in insert db for MSG_LOG_WATCH_END", ex);
+                    }
                     args.recycle();
                     break;
                 }
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
index b2db9f5..8dcc547 100644
--- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
+++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
@@ -23,7 +23,6 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
-import android.net.NetworkCapabilities.NetCapability;
 import android.net.NetworkRequest;
 import android.net.TelephonyNetworkSpecifier;
 import android.os.Handler;
@@ -115,33 +114,61 @@
                 getWifiNetworkRequest(), mHandler, mWifiBringupCallback);
         updateSubIdsAndCellularRequests();
 
-        // register Network-selection request used to decide selected underlying Network
+        // Register Network-selection request used to decide selected underlying Network. All
+        // underlying networks must be VCN managed in order to be used.
         mConnectivityManager.requestBackgroundNetwork(
-                getNetworkRequestBase().build(), mHandler, mRouteSelectionCallback);
+                getBaseNetworkRequest(true /* requireVcnManaged */).build(),
+                mHandler,
+                mRouteSelectionCallback);
     }
 
     private NetworkRequest getWifiNetworkRequest() {
-        return getNetworkRequestBase().addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
+        // Request exclusively VCN managed networks to ensure that we only ever keep carrier wifi
+        // alive.
+        return getBaseNetworkRequest(true /* requireVcnManaged */)
+                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                .build();
     }
 
     private NetworkRequest getCellNetworkRequestForSubId(int subId) {
-        return getNetworkRequestBase()
+        // Do not request NOT_VCN_MANAGED to ensure that the TelephonyNetworkFactory has a
+        // fulfillable request to bring up underlying cellular Networks even if the VCN is already
+        // connected.
+        return getBaseNetworkRequest(false /* requireVcnManaged */)
                 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                 .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
                 .build();
     }
 
-    private NetworkRequest.Builder getNetworkRequestBase() {
-        NetworkRequest.Builder requestBase = new NetworkRequest.Builder();
-        for (@NetCapability int capability : mRequiredUnderlyingNetworkCapabilities) {
+    /**
+     * Builds and returns a NetworkRequest builder common to all Underlying Network requests
+     *
+     * <p>A NetworkRequest may either (1) Require the presence of a capability by using
+     * addCapability(), (2) require the absence of a capability using unwanted capabilities, or (3)
+     * allow any state. Underlying networks are never desired to have the NOT_VCN_MANAGED
+     * capability, and only cases (2) and (3) are used.
+     *
+     * @param requireVcnManaged whether the underlying network is required to be VCN managed to
+     *     match this request. If {@code true}, the NOT_VCN_MANAGED capability will be set as
+     *     unwanted. Else, the NOT_VCN_MANAGED capability will be removed, and any state is
+     *     acceptable.
+     */
+    private NetworkRequest.Builder getBaseNetworkRequest(boolean requireVcnManaged) {
+        NetworkRequest.Builder requestBase =
+                new NetworkRequest.Builder()
+                        .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+                        .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+                        .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+
+        for (int capability : mRequiredUnderlyingNetworkCapabilities) {
             requestBase.addCapability(capability);
         }
 
-        return requestBase
-                .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
-                .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
-                .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
-                .addUnwantedCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+        if (requireVcnManaged) {
+            requestBase.addUnwantedCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+        }
+
+        return requestBase;
     }
 
     /**
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index c55913e..89ed956 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -41,6 +41,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -110,6 +111,24 @@
     @NonNull private final VcnNetworkRequestListener mRequestListener;
     @NonNull private final VcnCallback mVcnCallback;
 
+    /**
+     * Map containing all VcnGatewayConnections and their VcnGatewayConnectionConfigs.
+     *
+     * <p>Due to potential for race conditions, VcnGatewayConnections MUST only be created and added
+     * to this map in {@link #handleNetworkRequested(NetworkRequest, int, int)}, when a VCN receives
+     * a NetworkRequest that matches a VcnGatewayConnectionConfig for this VCN's VcnConfig.
+     *
+     * <p>A VcnGatewayConnection instance MUST NEVER overwrite an existing instance - otherwise
+     * there is potential for a orphaned VcnGatewayConnection instance that does not get properly
+     * shut down.
+     *
+     * <p>Due to potential for race conditions, VcnGatewayConnections MUST only be removed from this
+     * map once they have finished tearing down, which is reported to this VCN via {@link
+     * VcnGatewayStatusCallback#onQuit()}. Once this is done, all NetworkRequests are retrieved from
+     * the NetworkProvider so that another VcnGatewayConnectionConfig can match the
+     * previously-matched request.
+     */
+    // TODO(b/182533200): remove the invariant on VcnGatewayConnection lifecycles
     @NonNull
     private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections =
             new HashMap<>();
@@ -191,6 +210,19 @@
         return Collections.unmodifiableSet(new HashSet<>(mVcnGatewayConnections.values()));
     }
 
+    /** Get current Configs and Gateways for testing purposes */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public Map<VcnGatewayConnectionConfig, VcnGatewayConnection>
+            getVcnGatewayConnectionConfigMap() {
+        return Collections.unmodifiableMap(new HashMap<>(mVcnGatewayConnections));
+    }
+
+    /** Set whether this Vcn is active for testing purposes */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public void setIsActive(boolean isActive) {
+        mIsActive.set(isActive);
+    }
+
     private class VcnNetworkRequestListener implements VcnNetworkProvider.NetworkRequestListener {
         @Override
         public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
@@ -202,11 +234,6 @@
 
     @Override
     public void handleMessage(@NonNull Message msg) {
-        // Ignore if this Vcn is not active and we're not receiving new configs
-        if (!isActive() && msg.what != MSG_EVENT_CONFIG_UPDATED) {
-            return;
-        }
-
         switch (msg.what) {
             case MSG_EVENT_CONFIG_UPDATED:
                 handleConfigUpdated((VcnConfig) msg.obj);
@@ -237,9 +264,31 @@
 
         mConfig = config;
 
-        // TODO(b/181815405): Reevaluate active VcnGatewayConnection(s)
+        if (mIsActive.getAndSet(true)) {
+            // VCN is already active - teardown any GatewayConnections whose configs have been
+            // removed and get all current requests
+            for (final Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry :
+                    mVcnGatewayConnections.entrySet()) {
+                final VcnGatewayConnectionConfig gatewayConnectionConfig = entry.getKey();
+                final VcnGatewayConnection gatewayConnection = entry.getValue();
 
-        if (!mIsActive.getAndSet(true)) {
+                // GatewayConnectionConfigs must match exactly (otherwise authentication or
+                // connection details may have changed).
+                if (!mConfig.getGatewayConnectionConfigs().contains(gatewayConnectionConfig)) {
+                    if (gatewayConnection == null) {
+                        Slog.wtf(
+                                getLogTag(),
+                                "Found gatewayConnectionConfig without GatewayConnection");
+                    } else {
+                        gatewayConnection.teardownAsynchronously();
+                    }
+                }
+            }
+
+            // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be
+            // satisfied start a new GatewayConnection)
+            mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener);
+        } else {
             // If this VCN was not previously active, it is exiting Safe Mode. Re-register the
             // request listener to get NetworkRequests again (and all cached requests).
             mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener);
@@ -259,13 +308,16 @@
     private void handleEnterSafeMode() {
         handleTeardown();
 
-        mVcnGatewayConnections.clear();
-
         mVcnCallback.onEnteredSafeMode();
     }
 
     private void handleNetworkRequested(
             @NonNull NetworkRequest request, int score, int providerId) {
+        if (!isActive()) {
+            Slog.v(getLogTag(), "Received NetworkRequest while inactive. Ignore for now");
+            return;
+        }
+
         if (score > getNetworkScore()) {
             if (VDBG) {
                 Slog.v(
@@ -299,9 +351,7 @@
         for (VcnGatewayConnectionConfig gatewayConnectionConfig :
                 mConfig.getGatewayConnectionConfigs()) {
             if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
-                Slog.v(
-                        getLogTag(),
-                        "Bringing up new VcnGatewayConnection for request " + request.requestId);
+                Slog.v(getLogTag(), "Bringing up new VcnGatewayConnection for request " + request);
 
                 final VcnGatewayConnection vcnGatewayConnection =
                         mDeps.newVcnGatewayConnection(
@@ -320,8 +370,10 @@
         mVcnGatewayConnections.remove(config);
 
         // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be satisfied
-        // start a new GatewayConnection)
-        mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener);
+        // start a new GatewayConnection), but only if the Vcn is still active
+        if (isActive()) {
+            mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener);
+        }
     }
 
     private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) {
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 15429f4..9589505 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -20,6 +20,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.vcn.VcnManager.VCN_ERROR_CODE_CONFIG_ERROR;
@@ -41,7 +42,6 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkAgent;
-import android.net.NetworkAgent.ValidationStatus;
 import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
 import android.net.RouteInfo;
@@ -59,6 +59,7 @@
 import android.net.ipsec.ike.exceptions.IkeException;
 import android.net.ipsec.ike.exceptions.IkeInternalException;
 import android.net.ipsec.ike.exceptions.IkeProtocolException;
+import android.net.vcn.VcnControlPlaneIkeConfig;
 import android.net.vcn.VcnGatewayConnectionConfig;
 import android.net.vcn.VcnTransportInfo;
 import android.net.wifi.WifiInfo;
@@ -1348,7 +1349,7 @@
                 mIkeSession = null;
             }
 
-            mIkeSession = buildIkeSession();
+            mIkeSession = buildIkeSession(mUnderlying.network);
         }
 
         @Override
@@ -1440,17 +1441,16 @@
                             caps,
                             lp,
                             Vcn.getNetworkScore(),
-                            new NetworkAgentConfig(),
+                            new NetworkAgentConfig.Builder().build(),
                             mVcnContext.getVcnNetworkProvider()) {
                         @Override
-                        public void unwanted() {
+                        public void onNetworkUnwanted() {
                             Slog.d(TAG, "NetworkAgent was unwanted");
                             teardownAsynchronously();
                         }
 
                         @Override
-                        public void onValidationStatus(
-                                @ValidationStatus int status, @Nullable Uri redirectUri) {
+                        public void onValidationStatus(int status, @Nullable Uri redirectUri) {
                             if (status == NetworkAgent.VALIDATION_STATUS_VALID) {
                                 clearFailedAttemptCounterAndSafeModeAlarm();
                             }
@@ -1726,6 +1726,7 @@
         final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
 
         builder.addTransportType(TRANSPORT_CELLULAR);
+        builder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         builder.addCapability(NET_CAPABILITY_NOT_CONGESTED);
         builder.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
 
@@ -1795,8 +1796,10 @@
             lp.addDnsServer(addr);
         }
 
-        lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
-        lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
+        lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null /*gateway*/,
+                null /*iface*/, RouteInfo.RTN_UNICAST));
+        lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null /*gateway*/,
+                null /*iface*/, RouteInfo.RTN_UNICAST));
 
         lp.setMtu(gatewayConnectionConfig.getMaxMtu());
 
@@ -1939,23 +1942,29 @@
                 new EventDisconnectRequestedInfo(reason, shouldQuit));
     }
 
-    private IkeSessionParams buildIkeParams() {
-        // TODO: Implement this once IkeSessionParams is persisted
-        return null;
+    private IkeSessionParams buildIkeParams(@NonNull Network network) {
+        final VcnControlPlaneIkeConfig controlPlaneConfig =
+                (VcnControlPlaneIkeConfig) mConnectionConfig.getControlPlaneConfig();
+        final IkeSessionParams.Builder builder =
+                new IkeSessionParams.Builder(controlPlaneConfig.getIkeSessionParams());
+        builder.setConfiguredNetwork(network);
+
+        return builder.build();
     }
 
     private ChildSessionParams buildChildParams() {
-        // TODO: Implement this once IkeSessionParams is persisted
-        return null;
+        final VcnControlPlaneIkeConfig controlPlaneConfig =
+                (VcnControlPlaneIkeConfig) mConnectionConfig.getControlPlaneConfig();
+        return controlPlaneConfig.getChildSessionParams();
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
-    VcnIkeSession buildIkeSession() {
+    VcnIkeSession buildIkeSession(@NonNull Network network) {
         final int token = ++mCurrentToken;
 
         return mDeps.newIkeSession(
                 mVcnContext,
-                buildIkeParams(),
+                buildIkeParams(network),
                 buildChildParams(),
                 new IkeSessionCallbackImpl(token),
                 new VcnChildSessionCallback(token));
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 7e6b7cd..e060171 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -110,6 +110,7 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
@@ -165,13 +166,13 @@
     static final String TAG_TASKS = TAG + POSTFIX_TASKS;
 
     /** How long we wait until giving up on the last activity telling us it is idle. */
-    private static final int IDLE_TIMEOUT = 10 * 1000;
+    private static final int IDLE_TIMEOUT = 10 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
 
     /** How long we can hold the sleep wake lock before giving up. */
-    private static final int SLEEP_TIMEOUT = 5 * 1000;
+    private static final int SLEEP_TIMEOUT = 5 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
 
     // How long we can hold the launch wake lock before giving up.
-    private static final int LAUNCH_TIMEOUT = 10 * 1000;
+    private static final int LAUNCH_TIMEOUT = 10 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
 
     /** How long we wait until giving up on the activity telling us it released the top state. */
     private static final int TOP_RESUMED_STATE_LOSS_TIMEOUT = 500;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 816a6a0..f55a983 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -312,9 +312,10 @@
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
 
     // How long we wait until we timeout on key dispatching.
-    public static final int KEY_DISPATCHING_TIMEOUT_MS = 5 * 1000;
+    public static final int KEY_DISPATCHING_TIMEOUT_MS = 5 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
     // How long we wait until we timeout on key dispatching during instrumentation.
-    static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS = 60 * 1000;
+    static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS =
+            60 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
     // How long we permit background activity starts after an activity in the process
     // started or finished.
     static final long ACTIVITY_BG_START_GRACE_PERIOD_MS = 10 * 1000;
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 729fa71..d18043f 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -100,16 +100,9 @@
 }
 
 static void android_server_SystemServer_fdtrackAbort(JNIEnv*, jobject) {
-    raise(BIONIC_SIGNAL_FDTRACK);
-
-    // Wait for a bit to allow fdtrack to dump backtraces to logcat.
-    std::this_thread::sleep_for(5s);
-
-    // Abort on a different thread to avoid ART dumping runtime stacks.
-    std::thread([]() {
-        LOG_ALWAYS_FATAL("b/140703823: aborting due to fd leak: check logs for fd "
-                         "backtraces");
-    }).join();
+    sigval val;
+    val.sival_int = 1;
+    sigqueue(getpid(), BIONIC_SIGNAL_FDTRACK, val);
 }
 
 static jlong android_server_SystemServer_startIncrementalService(JNIEnv* env, jclass klass,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6badafa..736a6f6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -15621,10 +15621,7 @@
 
         Objects.requireNonNull(who, "ComponentName is null");
         enforceDeviceOwner(who);
-        String currentMode = mInjector.settingsGlobalGetString(PRIVATE_DNS_MODE);
-        if (currentMode == null) {
-            currentMode = ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
-        }
+        final String currentMode = ConnectivityManager.getPrivateDnsMode(mContext);
         switch (currentMode) {
             case ConnectivityManager.PRIVATE_DNS_MODE_OFF:
                 return PRIVATE_DNS_MODE_OFF;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a6a99f23..46ec65d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -108,6 +108,7 @@
 import com.android.server.clipboard.ClipboardService;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.compat.PlatformCompatNative;
+import com.android.server.connectivity.PacProxyService;
 import com.android.server.contentcapture.ContentCaptureManagerInternal;
 import com.android.server.coverage.CoverageService;
 import com.android.server.devicepolicy.DevicePolicyManagerService;
@@ -331,8 +332,6 @@
     private static final String UNCRYPT_PACKAGE_FILE = "/cache/recovery/uncrypt_file";
     private static final String BLOCK_MAP_FILE = "/cache/recovery/block.map";
 
-    private static final String GSI_RUNNING_PROP = "ro.gsid.image_running";
-
     // maximum number of binder threads used for system_server
     // will be higher than the system default
     private static final int sMaxBinderThreads = 31;
@@ -1115,6 +1114,7 @@
         ConsumerIrService consumerIr = null;
         MmsServiceBroker mmsService = null;
         HardwarePropertiesManagerService hardwarePropertiesService = null;
+        PacProxyService pacProxyService = null;
 
         boolean disableSystemTextClassifier = SystemProperties.getBoolean(
                 "config.disable_systemtextclassifier", false);
@@ -1125,7 +1125,7 @@
                 false);
         boolean enableLeftyService = SystemProperties.getBoolean("config.enable_lefty", false);
 
-        boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
+        boolean isEmulator = SystemProperties.get("ro.boot.qemu").equals("1");
 
         boolean isWatch = context.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_WATCH);
@@ -1441,8 +1441,7 @@
             t.traceEnd();
 
             final boolean hasPdb = !SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP).equals("");
-            final boolean hasGsi = SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0;
-            if (hasPdb && !hasGsi) {
+            if (hasPdb) {
                 t.traceBegin("StartPersistentDataBlock");
                 mSystemServiceManager.startService(PersistentDataBlockService.class);
                 t.traceEnd();
@@ -1623,6 +1622,15 @@
                 t.traceEnd();
             }
 
+            t.traceBegin("StartPacProxyService");
+            try {
+                pacProxyService = new PacProxyService(context);
+                ServiceManager.addService(Context.PAC_PROXY_SERVICE, pacProxyService);
+            } catch (Throwable e) {
+                reportWtf("starting PacProxyService", e);
+            }
+            t.traceEnd();
+
             t.traceBegin("StartConnectivityService");
             // This has to be called after NetworkManagementService, NetworkStatsService
             // and NetworkPolicyManager because ConnectivityService needs to take these
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index a262939..29aedce 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -295,10 +295,30 @@
             return;
         }
 
-        try {
-            mIProfcollect.report();
-        } catch (RemoteException e) {
-            Log.e(LOG_TAG, e.getMessage());
-        }
+        final boolean uploadReport =
+                DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
+                                        "upload_report", false);
+
+        new Thread(() -> {
+            try {
+                String reportPath = mIProfcollect.report();
+                if (!uploadReport) {
+                    return;
+                }
+                Intent uploadIntent =
+                        new Intent("com.google.android.apps.betterbug.intent.action.UPLOAD_PROFILE")
+                        .setPackage("com.google.android.apps.internal.betterbug")
+                        .putExtra("EXTRA_DESTINATION", "PROFCOLLECT")
+                        .putExtra("EXTRA_PACKAGE_NAME", getContext().getPackageName())
+                        .putExtra("EXTRA_PROFILE_PATH", reportPath)
+                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                Context context = getContext();
+                if (context.getPackageManager().queryBroadcastReceivers(uploadIntent, 0) != null) {
+                    context.sendBroadcast(uploadIntent);
+                }
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, e.getMessage());
+            }
+        }).start();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java
similarity index 84%
rename from services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
rename to services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java
index ee0a16a..2e0cadf 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.pm.dex;
@@ -28,28 +28,34 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.BatteryManager;
 import android.os.Build;
+import android.os.PowerManager;
 import android.os.UserHandle;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
 import com.android.server.pm.Installer;
+import com.android.server.pm.PackageManagerService;
 
 import dalvik.system.DelegateLastClassLoader;
 import dalvik.system.PathClassLoader;
 import dalvik.system.VMRuntime;
 
+import org.junit.After;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
 import org.mockito.quality.Strictness;
 
 import java.io.File;
@@ -69,9 +75,15 @@
             DelegateLastClassLoader.class.getName();
     private static final String UNSUPPORTED_CLASS_LOADER_NAME = "unsupported.class_loader";
 
-    @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+    private static final int TEST_BATTERY_LEVEL_CRITICAL = 10;
+    private static final int TEST_BATTERY_LEVEL_DEFAULT = 80;
+
+    public StaticMockitoSession mMockitoSession;
     @Mock Installer mInstaller;
     @Mock IPackageManager mPM;
+    @Mock BatteryManager mMockBatteryManager;
+    @Mock PowerManager mMockPowerManager;
+
     private final Object mInstallLock = new Object();
 
     private DexManager mDexManager;
@@ -117,7 +129,37 @@
         mSystemServerJarUpdatedContext = new TestData("android", isa, mUser0,
                 DELEGATE_LAST_CLASS_LOADER_NAME);
 
-        mDexManager = new DexManager(/*Context*/ null, mPM, /*PackageDexOptimizer*/ null,
+        // Initialize Static Mocking
+
+        mMockitoSession = ExtendedMockito.mockitoSession()
+            .initMocks(this)
+            .strictness(Strictness.LENIENT)
+            .startMocking();
+
+        // Mock....
+
+        mMockBatteryManager = ExtendedMockito.mock(BatteryManager.class);
+        mMockPowerManager   = ExtendedMockito.mock(PowerManager.class);
+
+        setDefaultMockValues();
+
+        Resources mockResources = ExtendedMockito.mock(Resources.class);
+        ExtendedMockito.when(mockResources
+            .getInteger(com.android.internal.R.integer.config_criticalBatteryWarningLevel))
+                .thenReturn(15);
+
+        Context mockContext = ExtendedMockito.mock(Context.class);
+        ExtendedMockito.doReturn(mockResources)
+            .when(mockContext)
+                .getResources();
+        ExtendedMockito.doReturn(mMockBatteryManager)
+            .when(mockContext)
+                .getSystemService(BatteryManager.class);
+        ExtendedMockito.doReturn(mMockPowerManager)
+            .when(mockContext)
+                .getSystemService(PowerManager.class);
+
+        mDexManager = new DexManager(mockContext, mPM, /*PackageDexOptimizer*/ null,
                 mInstaller, mInstallLock);
 
         // Foo and Bar are available to user0.
@@ -128,6 +170,25 @@
         mDexManager.load(existingPackages);
     }
 
+    @After
+    public void teardown() throws Exception {
+        mMockitoSession.finishMocking();
+    }
+
+    private void setDefaultMockValues() {
+        ExtendedMockito.doReturn(BatteryManager.BATTERY_STATUS_DISCHARGING)
+            .when(mMockBatteryManager)
+                .getIntProperty(BatteryManager.BATTERY_PROPERTY_STATUS);
+
+        ExtendedMockito.doReturn(TEST_BATTERY_LEVEL_DEFAULT)
+            .when(mMockBatteryManager)
+                .getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
+
+        ExtendedMockito.doReturn(PowerManager.THERMAL_STATUS_NONE)
+            .when(mMockPowerManager)
+                .getCurrentThermalStatus();
+    }
+
     @Test
     public void testNotifyPrimaryUse() {
         // The main dex file and splits are re-loaded by the app.
@@ -633,6 +694,114 @@
         assertNoDclInfo(mSystemServerJarInvalid);
     }
 
+    @Test
+    public void testInstallScenarioToReasonDefault() {
+        assertEquals(
+                PackageManagerService.REASON_INSTALL,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_DEFAULT));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_FAST,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_FAST));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_BULK,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_BULK));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_BULK_SECONDARY,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_BULK_SECONDARY));
+    }
+
+    @Test
+    public void testInstallScenarioToReasonThermal() {
+        ExtendedMockito.doReturn(PowerManager.THERMAL_STATUS_SEVERE)
+            .when(mMockPowerManager)
+                .getCurrentThermalStatus();
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_DEFAULT));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_FAST,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_FAST));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_BULK_DOWNGRADED,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_BULK));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_BULK_SECONDARY));
+    }
+
+    @Test
+    public void testInstallScenarioToReasonBatteryDischarging() {
+        ExtendedMockito.doReturn(TEST_BATTERY_LEVEL_CRITICAL)
+            .when(mMockBatteryManager)
+                .getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_DEFAULT));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_FAST,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_FAST));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_BULK_DOWNGRADED,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_BULK));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_BULK_SECONDARY));
+    }
+
+    @Test
+    public void testInstallScenarioToReasonBatteryCharging() {
+        ExtendedMockito.doReturn(TEST_BATTERY_LEVEL_CRITICAL)
+            .when(mMockBatteryManager)
+                .getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
+
+        ExtendedMockito.doReturn(BatteryManager.BATTERY_STATUS_CHARGING)
+            .when(mMockBatteryManager)
+                .getIntProperty(BatteryManager.BATTERY_PROPERTY_STATUS);
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_DEFAULT));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_FAST,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_FAST));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_BULK,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_BULK));
+
+        assertEquals(
+                PackageManagerService.REASON_INSTALL_BULK_SECONDARY,
+                mDexManager.getCompilationReasonForInstallScenario(
+                        PackageManager.INSTALL_SCENARIO_BULK_SECONDARY));
+    }
+
     private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
             List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
             String[] expectedContexts) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/dex/OWNERS b/services/tests/mockingservicestests/src/com/android/server/pm/dex/OWNERS
new file mode 100644
index 0000000..5492dc8
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/dex/OWNERS
@@ -0,0 +1 @@
+include platform/art:/OWNERS
\ No newline at end of file
diff --git a/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/OWNERS b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/OWNERS
new file mode 100644
index 0000000..e95633a
--- /dev/null
+++ b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/admin/OWNERS
diff --git a/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest2/OWNERS b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest2/OWNERS
new file mode 100644
index 0000000..e95633a
--- /dev/null
+++ b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest2/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/admin/OWNERS
diff --git a/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest3/OWNERS b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest3/OWNERS
new file mode 100644
index 0000000..e95633a
--- /dev/null
+++ b/services/tests/servicestests/assets/DevicePolicyManagerServiceMigrationTest3/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/admin/OWNERS
diff --git a/services/tests/servicestests/assets/OwnersTest/OWNERS b/services/tests/servicestests/assets/OwnersTest/OWNERS
new file mode 100644
index 0000000..e95633a
--- /dev/null
+++ b/services/tests/servicestests/assets/OwnersTest/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/admin/OWNERS
diff --git a/services/tests/servicestests/assets/PolicyVersionUpgraderTest/OWNERS b/services/tests/servicestests/assets/PolicyVersionUpgraderTest/OWNERS
new file mode 100644
index 0000000..e95633a
--- /dev/null
+++ b/services/tests/servicestests/assets/PolicyVersionUpgraderTest/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/admin/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
index 07f6732..2f0d71a 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -18,6 +18,7 @@
 
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.AdditionalAnswers.returnsArgAt;
 import static org.mockito.ArgumentMatchers.any;
@@ -40,9 +41,11 @@
 import android.net.Uri;
 import android.os.RemoteException;
 import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
 import org.junit.Before;
@@ -55,15 +58,18 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * Tests for {@link com.android.server.apphibernation.AppHibernationService}
  */
 @SmallTest
+@Presubmit
 public final class AppHibernationServiceTest {
     private static final String PACKAGE_SCHEME = "package";
     private static final String PACKAGE_NAME_1 = "package1";
     private static final String PACKAGE_NAME_2 = "package2";
+    private static final String PACKAGE_NAME_3 = "package3";
     private static final int USER_ID_1 = 1;
     private static final int USER_ID_2 = 2;
 
@@ -91,6 +97,7 @@
         MockitoAnnotations.initMocks(this);
         doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
 
+        LocalServices.removeServiceForTest(AppHibernationManagerInternal.class);
         mAppHibernationService = new AppHibernationService(new MockInjector(mContext));
 
         verify(mContext).registerReceiver(mReceiverCaptor.capture(), any());
@@ -103,13 +110,15 @@
 
         List<PackageInfo> packages = new ArrayList<>();
         packages.add(makePackageInfo(PACKAGE_NAME_1));
+        packages.add(makePackageInfo(PACKAGE_NAME_2));
+        packages.add(makePackageInfo(PACKAGE_NAME_3));
         doReturn(new ParceledListSlice<>(packages)).when(mIPackageManager).getInstalledPackages(
                 intThat(arg -> (arg & MATCH_ANY_USER) != 0), anyInt());
         mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
 
         UserInfo userInfo = addUser(USER_ID_1);
-        mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));
         doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_1);
+        mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));
 
         mAppHibernationService.mIsServiceEnabled = true;
     }
@@ -142,8 +151,8 @@
             throws RemoteException {
         // WHEN a new user is added and a package from the user is hibernated
         UserInfo user2 = addUser(USER_ID_2);
-        mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2));
         doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2);
+        mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2));
         mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true);
 
         // THEN the new user's package is hibernated
@@ -175,6 +184,26 @@
         assertTrue(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1));
     }
 
+    @Test
+    public void testGetHibernatingPackagesForUser_returnsCorrectPackages() throws RemoteException {
+        // GIVEN an unlocked user with all packages installed
+        UserInfo userInfo =
+                addUser(USER_ID_2, new String[]{PACKAGE_NAME_1, PACKAGE_NAME_2, PACKAGE_NAME_3});
+        doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2);
+        mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));
+
+        // WHEN packages are hibernated for the user
+        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true);
+        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_2, USER_ID_2, true);
+
+        // THEN the hibernating packages returned matches
+        List<String> hibernatingPackages =
+                mAppHibernationService.getHibernatingPackagesForUser(USER_ID_2);
+        assertEquals(2, hibernatingPackages.size());
+        assertTrue(hibernatingPackages.contains(PACKAGE_NAME_1));
+        assertTrue(hibernatingPackages.contains(PACKAGE_NAME_2));
+    }
+
     /**
      * Add a mock user with one package.
      */
@@ -231,6 +260,12 @@
         }
 
         @Override
+        public Executor getBackgroundExecutor() {
+            // Just execute immediately in tests.
+            return r -> r.run();
+        }
+
+        @Override
         public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() {
             return Mockito.mock(HibernationStateDiskStore.class);
         }
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java
index 59f3c35..2237c84 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 
 import android.os.FileUtils;
+import android.platform.test.annotations.Presubmit;
 import android.util.proto.ProtoInputStream;
 import android.util.proto.ProtoOutputStream;
 
@@ -48,6 +49,7 @@
 
 
 @SmallTest
+@Presubmit
 public class HibernationStateDiskStoreTest {
     private static final String STATES_FILE_NAME = "states";
     private final MockScheduledExecutorService mMockScheduledExecutorService =
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
index f00edcc..7bdc87e 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
@@ -116,7 +116,12 @@
     }
 
     CompatConfigBuilder addOverridableChangeWithId(long id) {
-        mChanges.add(new CompatChange(id, "", -1, -1, false, true, "", true));
+        mChanges.add(new CompatChange(id, "", -1, -1, true, false, "", true));
+        return this;
+    }
+
+    CompatConfigBuilder addEnabledSinceApexChangeWithId(int sdk, long id) {
+        mChanges.add(new CompatChange(id, "", -1, sdk, false, false, "", false));
         return this;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index 8b0e948..a866363 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -86,6 +86,7 @@
         // Assume userdebug/eng non-final build
         when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
         when(mBuildClassifier.isFinalBuild()).thenReturn(false);
+        when(mBuildClassifier.platformTargetSdk()).thenReturn(30);
         ChangeIdStateCache.disable();
         when(mPackageManager.getApplicationInfo(anyString(), anyInt()))
                 .thenThrow(new NameNotFoundException());
@@ -259,6 +260,36 @@
     }
 
     @Test
+    public void testInstallerCanSetOverrides() throws Exception {
+        final long changeId = 1234L;
+        final int installerUid = 23;
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addOverridableChangeWithId(1234L)
+                .build();
+        ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+                .withPackageName("com.some.package")
+                .build();
+        PackageManager packageManager = mock(PackageManager.class);
+        when(mContext.getPackageManager()).thenReturn(packageManager);
+        when(packageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
+                .thenReturn(applicationInfo);
+
+        // Force the validator to prevent overriding the change by using a user build.
+        when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
+        when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+
+        CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(
+                Collections.singletonMap(1234L,
+                        new PackageOverride.Builder()
+                                .setMaxVersionCode(99L)
+                                .setEnabled(true)
+                                .build()));
+
+        compatConfig.addOverrides(config, "com.some.package");
+        assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue();
+    }
+
+    @Test
     public void testApplyDeferredOverridesAfterInstallingApp() throws Exception {
         ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
                 .withPackageName("com.notinstalled.foo")
@@ -567,6 +598,34 @@
     }
 
     @Test
+    public void testReadApexConfig() throws IOException {
+        String configXml = "<config>"
+                + "<compat-change id=\"1234\" name=\"MY_CHANGE1\" enableAfterTargetSdk=\"2\" />"
+                + "<compat-change id=\"1235\" name=\"MY_CHANGE2\" disabled=\"true\" />"
+                + "<compat-change id=\"1236\" name=\"MY_CHANGE3\" />"
+                + "<compat-change id=\"1237\" name=\"MY_CHANGE4\" enableSinceTargetSdk=\"31\" />"
+                + "</config>";
+
+        File dir = createTempDir();
+        writeToFile(dir, "platform_compat_config.xml", configXml);
+        CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+        compatConfig.forceNonDebuggableFinalForTest(false);
+
+        compatConfig.initConfigFromLib(dir);
+
+        assertThat(compatConfig.isChangeEnabled(1234L,
+            ApplicationInfoBuilder.create().withTargetSdk(1).build())).isFalse();
+        assertThat(compatConfig.isChangeEnabled(1234L,
+            ApplicationInfoBuilder.create().withTargetSdk(3).build())).isTrue();
+        assertThat(compatConfig.isChangeEnabled(1235L,
+            ApplicationInfoBuilder.create().withTargetSdk(5).build())).isFalse();
+        assertThat(compatConfig.isChangeEnabled(1236L,
+            ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue();
+        assertThat(compatConfig.isChangeEnabled(1237L,
+            ApplicationInfoBuilder.create().withTargetSdk(31).build())).isTrue();
+    }
+
+    @Test
     public void testReadConfigMultipleFiles() throws IOException {
         String configXml1 = "<config>"
                 + "<compat-change id=\"1234\" name=\"MY_CHANGE1\" enableAfterTargetSdk=\"2\" />"
@@ -602,17 +661,26 @@
                 .addEnableSinceSdkChangeWithId(2, 2L)
                 .build();
         compatConfig.forceNonDebuggableFinalForTest(true);
-        compatConfig.initOverrides(overridesFile);
+        compatConfig.initOverrides(overridesFile, new File(""));
         when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
                 .thenReturn(ApplicationInfoBuilder.create()
-                                .withPackageName("foo.bar")
-                                .debuggable()
-                                .build());
+                        .withPackageName("foo.bar")
+                        .debuggable()
+                        .build());
         when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
                 .thenThrow(new NameNotFoundException());
-
-        compatConfig.addOverride(1L, "foo.bar", true);
-        compatConfig.addOverride(2L, "bar.baz", false);
+        compatConfig.addOverrides(
+                new CompatibilityOverrideConfig(
+                        Collections.singletonMap(
+                                1L,
+                                new PackageOverride.Builder().setEnabled(true).build())),
+                "foo.bar");
+        compatConfig.addOverrides(
+                new CompatibilityOverrideConfig(
+                        Collections.singletonMap(
+                                2L,
+                                new PackageOverride.Builder().setEnabled(false).build())),
+                "bar.baz");
 
         assertThat(readFile(overridesFile)).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                 + "<overrides>\n"
@@ -649,7 +717,7 @@
                 .addEnableSinceSdkChangeWithId(2, 2L)
                 .build();
         compatConfig.forceNonDebuggableFinalForTest(true);
-        compatConfig.initOverrides(overridesFile);
+        compatConfig.initOverrides(overridesFile, new File(""));
 
         compatConfig.addOverrides(new CompatibilityOverrideConfig(Collections.singletonMap(1L,
                 new PackageOverride.Builder()
@@ -673,11 +741,11 @@
     }
 
     @Test
-    public void testLoadOverridesRaw() throws Exception {
+    public void testInitOverridesRaw() throws Exception {
         File tempDir = createTempDir();
         File overridesFile = new File(tempDir, "overrides.xml");
         // Change 1 is enabled for foo.bar (validated)
-        // Change 2 is disabled for bar.baz (deferred)
+        // Change 2 is disabled for bar.baz (raw)
         String xmlData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
                 + "<overrides>\n"
                 + "    <change-overrides changeId=\"1\">\n"
@@ -709,7 +777,7 @@
                 .addEnableSinceSdkChangeWithId(2, 2L)
                 .build();
         compatConfig.forceNonDebuggableFinalForTest(true);
-        compatConfig.initOverrides(overridesFile);
+        compatConfig.initOverrides(overridesFile, new File(""));
         ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
                 .withPackageName("foo.bar")
                 .withVersionCode(100L)
@@ -728,7 +796,7 @@
     }
 
     @Test
-    public void testLoadOverridesDeferred() throws Exception {
+    public void testInitOverridesDeferred() throws Exception {
         File tempDir = createTempDir();
         File overridesFile = new File(tempDir, "overrides.xml");
         // Change 1 is enabled for foo.bar (validated)
@@ -754,7 +822,7 @@
                 .addEnableSinceSdkChangeWithId(2, 2L)
                 .build();
         compatConfig.forceNonDebuggableFinalForTest(true);
-        compatConfig.initOverrides(overridesFile);
+        compatConfig.initOverrides(overridesFile, new File(""));
         ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
                 .withPackageName("foo.bar")
                 .debuggable()
@@ -767,4 +835,115 @@
         assertThat(compatConfig.isChangeEnabled(1L, applicationInfo)).isTrue();
         assertThat(compatConfig.willChangeBeEnabled(2L, "bar.baz")).isFalse();
     }
+
+    @Test
+    public void testInitOverridesWithStaticFile() throws Exception {
+        File tempDir = createTempDir();
+        File dynamicOverridesFile = new File(tempDir, "dynamic_overrides.xml");
+        File staticOverridesFile = new File(tempDir, "static_overrides.xml");
+        // Change 1 is enabled for foo.bar (raw)
+        // Change 2 is disabled for bar.baz (raw)
+        String dynamicXmlData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+                + "<overrides>"
+                +    "<change-overrides changeId=\"1\">"
+                +        "<raw>"
+                + "            <raw-override-value packageName=\"foo.bar\" "
+                + "minVersionCode=\"-9223372036854775808\" "
+                + "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+                + "            </raw-override-value>\n"
+                +        "</raw>"
+                +    "</change-overrides>"
+                +    "<change-overrides changeId=\"2\">"
+                +        "<raw>"
+                + "            <raw-override-value packageName=\"bar.baz\" "
+                + "minVersionCode=\"-9223372036854775808\" "
+                + "maxVersionCode=\"9223372036854775807\" enabled=\"false\">\n"
+                + "            </raw-override-value>\n"
+                +        "</raw>"
+                +    "</change-overrides>"
+                + "</overrides>";
+        writeToFile(tempDir, "dynamic_overrides.xml", dynamicXmlData);
+        // Change 2 is enabled for foo.bar and bar.baz (raw)
+        // Change 3 is enabled for bar.baz (raw)
+        String staticXmlData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+                + "<overrides>"
+                +    "<change-overrides changeId=\"2\">"
+                +        "<raw>"
+                + "            <raw-override-value packageName=\"foo.bar\" "
+                + "minVersionCode=\"-9223372036854775808\" "
+                + "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+                + "            </raw-override-value>\n"
+                + "            <raw-override-value packageName=\"bar.baz\" "
+                + "minVersionCode=\"-9223372036854775808\" "
+                + "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+                + "            </raw-override-value>\n"
+                +        "</raw>"
+                +    "</change-overrides>"
+                +    "<change-overrides changeId=\"3\">"
+                +        "<raw>"
+                + "            <raw-override-value packageName=\"bar.baz\" "
+                + "minVersionCode=\"-9223372036854775808\" "
+                + "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+                + "            </raw-override-value>\n"
+                +        "</raw>"
+                +    "</change-overrides>"
+                + "</overrides>";
+        writeToFile(tempDir, "static_overrides.xml", staticXmlData);
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addDisabledChangeWithId(1L)
+                .addDisabledChangeWithId(2L)
+                .addDisabledChangeWithId(3L)
+                .build();
+        compatConfig.forceNonDebuggableFinalForTest(true);
+        // Adding an override that will be cleared after initOverrides is called.
+        compatConfig.addOverride(1L, "bar.baz", true);
+        compatConfig.initOverrides(dynamicOverridesFile, staticOverridesFile);
+        when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
+                .thenThrow(new NameNotFoundException());
+        when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
+                .thenThrow(new NameNotFoundException());
+
+        assertThat(compatConfig.willChangeBeEnabled(1L, "foo.bar")).isTrue();
+        assertThat(compatConfig.willChangeBeEnabled(2L, "foo.bar")).isTrue();
+        assertThat(compatConfig.willChangeBeEnabled(2L, "bar.baz")).isFalse();
+        assertThat(compatConfig.willChangeBeEnabled(3L, "bar.baz")).isTrue();
+        assertThat(readFile(dynamicOverridesFile))
+                .isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                + "<overrides>\n"
+                + "    <change-overrides changeId=\"1\">\n"
+                + "        <validated>\n"
+                + "        </validated>\n"
+                + "        <raw>\n"
+                + "            <raw-override-value packageName=\"foo.bar\" "
+                + "minVersionCode=\"-9223372036854775808\" "
+                + "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+                + "            </raw-override-value>\n"
+                + "        </raw>\n"
+                + "    </change-overrides>\n"
+                + "    <change-overrides changeId=\"2\">\n"
+                + "        <validated>\n"
+                + "        </validated>\n"
+                + "        <raw>\n"
+                + "            <raw-override-value packageName=\"foo.bar\" "
+                + "minVersionCode=\"-9223372036854775808\" "
+                + "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+                + "            </raw-override-value>\n"
+                + "            <raw-override-value packageName=\"bar.baz\" "
+                + "minVersionCode=\"-9223372036854775808\" "
+                + "maxVersionCode=\"9223372036854775807\" enabled=\"false\">\n"
+                + "            </raw-override-value>\n"
+                + "        </raw>\n"
+                + "    </change-overrides>\n"
+                + "    <change-overrides changeId=\"3\">\n"
+                + "        <validated>\n"
+                + "        </validated>\n"
+                + "        <raw>\n"
+                + "            <raw-override-value packageName=\"bar.baz\" "
+                + "minVersionCode=\"-9223372036854775808\" "
+                + "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+                + "            </raw-override-value>\n"
+                + "        </raw>\n"
+                + "    </change-overrides>\n"
+                + "</overrides>\n");
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
index 0fd6445..57fdcd3 100644
--- a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
@@ -22,6 +22,7 @@
 import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE;
 import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH;
 import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE;
+import static com.android.internal.compat.OverrideAllowedState.PLATFORM_TOO_OLD;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -52,6 +53,7 @@
     private static final int TARGET_SDK = 10;
     private static final int TARGET_SDK_BEFORE = 9;
     private static final int TARGET_SDK_AFTER = 11;
+    private static final int PLATFORM_SDK_VERSION = 30;
 
     @Mock
     private PackageManager mPackageManager;
@@ -61,6 +63,7 @@
     private AndroidBuildClassifier debuggableBuild() {
         AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
         when(buildClassifier.isDebuggableBuild()).thenReturn(true);
+        when(buildClassifier.platformTargetSdk()).thenReturn(PLATFORM_SDK_VERSION);
         return buildClassifier;
     }
 
@@ -68,6 +71,7 @@
         AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
         when(buildClassifier.isDebuggableBuild()).thenReturn(false);
         when(buildClassifier.isFinalBuild()).thenReturn(false);
+        when(buildClassifier.platformTargetSdk()).thenReturn(PLATFORM_SDK_VERSION);
         return buildClassifier;
     }
 
@@ -75,6 +79,7 @@
         AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
         when(buildClassifier.isDebuggableBuild()).thenReturn(false);
         when(buildClassifier.isFinalBuild()).thenReturn(true);
+        when(buildClassifier.platformTargetSdk()).thenReturn(PLATFORM_SDK_VERSION);
         return buildClassifier;
     }
 
@@ -333,6 +338,26 @@
     }
 
     @Test
+    public void getOverrideAllowedState_targetSdkChangeGreaterThanOsVersion_rejectOverride()
+            throws Exception {
+        final AndroidBuildClassifier buildClassifier = finalBuild();
+        CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
+                        .addEnabledSinceApexChangeWithId(PLATFORM_SDK_VERSION + 1, 1).build();
+        IOverrideValidator overrideValidator = config.getOverrideValidator();
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(ApplicationInfoBuilder.create()
+                        .withPackageName(PACKAGE_NAME)
+                        .debuggable()
+                        .build());
+
+        OverrideAllowedState stateTargetSdkLessChange =
+                overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+        assertThat(stateTargetSdkLessChange).isEqualTo(
+                new OverrideAllowedState(PLATFORM_TOO_OLD, -1,
+                                         PLATFORM_SDK_VERSION));
+    }
+
+    @Test
     public void getOverrideAllowedState_finalBuildEnabledChangeDebugApp_rejectOverride()
             throws Exception {
         CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index 799b067..a2664e5 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -78,11 +78,12 @@
         when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
             .thenThrow(new PackageManager.NameNotFoundException());
         mCompatConfig = new CompatConfig(mBuildClassifier, mContext);
-        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
         // Assume userdebug/eng non-final build
         mCompatConfig.forceNonDebuggableFinalForTest(false);
         when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
         when(mBuildClassifier.isFinalBuild()).thenReturn(false);
+        when(mBuildClassifier.platformTargetSdk()).thenReturn(30);
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
         LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
     }
@@ -99,7 +100,7 @@
                 .addLoggingOnlyChangeWithId(7L)
                 .addOverridableChangeWithId(8L)
                 .build();
-        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
         assertThat(mPlatformCompat.listAllChanges()).asList().containsExactly(
                 new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false),
                 new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false),
@@ -112,7 +113,7 @@
                 new CompatibilityChangeInfo(
                         6L, "", Build.VERSION_CODES.R, -1, false, false, "", false),
                 new CompatibilityChangeInfo(7L, "", -1, -1, false, true, "", false),
-                new CompatibilityChangeInfo(8L, "", -1, -1, false, true, "", true));
+                new CompatibilityChangeInfo(8L, "", -1, -1, true, false, "", true));
     }
 
     @Test
@@ -125,8 +126,9 @@
                 .addEnableSinceSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
                 .addEnableSinceSdkChangeWithId(Build.VERSION_CODES.R, 6L)
                 .addLoggingOnlyChangeWithId(7L)
+                .addEnableSinceSdkChangeWithId(31, 8L)
                 .build();
-        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
         assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly(
                 new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false),
                 new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false),
@@ -144,7 +146,7 @@
                 .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.O, 3L)
                 .build();
         mCompatConfig.forceNonDebuggableFinalForTest(true);
-        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
 
         // Before adding overrides.
         assertThat(mPlatformCompat.isChangeEnabledByPackageName(1, PACKAGE_NAME, 0)).isTrue();
diff --git a/services/tests/servicestests/src/com/android/server/content/OWNERS b/services/tests/servicestests/src/com/android/server/content/OWNERS
new file mode 100644
index 0000000..6264a142
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/content/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/content/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index b51f4df..8c08226 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -21,11 +21,13 @@
 import static android.content.pm.UserInfo.FLAG_PROFILE;
 import static android.os.UserHandle.USER_SYSTEM;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
@@ -109,7 +111,12 @@
     public interface MockableRebootEscrowInjected {
         int getBootCount();
 
-        void reportMetric(boolean success);
+        long getCurrentTimeMillis();
+
+        boolean forceServerBased();
+
+        void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount,
+                int escrowDurationInSeconds, int vbmetaDigestStatus, int durationSinceBootComplete);
     }
 
     static class MockInjector extends RebootEscrowManager.Injector {
@@ -119,6 +126,7 @@
         private final UserManager mUserManager;
         private final MockableRebootEscrowInjected mInjected;
         private final RebootEscrowKeyStoreManager mKeyStoreManager;
+        private final boolean mServerBased;
 
         MockInjector(Context context, UserManager userManager,
                 IRebootEscrow rebootEscrow,
@@ -128,6 +136,7 @@
             super(context, storage);
             mRebootEscrow = rebootEscrow;
             mServiceConnection = null;
+            mServerBased = false;
             RebootEscrowProviderHalImpl.Injector halInjector =
                     new RebootEscrowProviderHalImpl.Injector() {
                         @Override
@@ -149,6 +158,7 @@
             super(context, storage);
             mServiceConnection = serviceConnection;
             mRebootEscrow = null;
+            mServerBased = true;
             RebootEscrowProviderServerBasedImpl.Injector injector =
                     new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection);
             mRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(storage, injector);
@@ -168,6 +178,14 @@
         }
 
         @Override
+        public boolean serverBasedResumeOnReboot() {
+            if (mInjected.forceServerBased()) {
+                return true;
+            }
+            return mServerBased;
+        }
+
+        @Override
         public RebootEscrowProviderInterface getRebootEscrowProvider() {
             return mRebootEscrowProvider;
         }
@@ -195,8 +213,22 @@
         }
 
         @Override
-        public void reportMetric(boolean success) {
-            mInjected.reportMetric(success);
+        public String getVbmetaDigest(boolean other) {
+            return other ? "" : "fake digest";
+        }
+
+        @Override
+        public long getCurrentTimeMillis() {
+            return mInjected.getCurrentTimeMillis();
+        }
+
+        @Override
+        public void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount,
+                int escrowDurationInSeconds, int vbmetaDigestStatus,
+                int durationSinceBootComplete) {
+
+            mInjected.reportMetric(success, errorCode, serviceType, attemptCount,
+                    escrowDurationInSeconds, vbmetaDigestStatus, durationSinceBootComplete);
         }
     }
 
@@ -417,14 +449,21 @@
         // pretend reboot happens here
 
         when(mInjected.getBootCount()).thenReturn(1);
+        when(mInjected.getCurrentTimeMillis()).thenReturn(30000L);
+        mStorage.setLong(RebootEscrowManager.REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, 10000L,
+                USER_SYSTEM);
         ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
-        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
+        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
+                eq(0) /* error code */, eq(1) /* HAL based */, eq(1) /* attempt count */,
+                eq(20), eq(0) /* vbmeta status */, anyInt());
         when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue());
 
         mService.loadRebootEscrowDataIfAvailable(null);
         verify(mRebootEscrow).retrieveKey();
         assertTrue(metricsSuccessCaptor.getValue());
         verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
+        assertEquals(mStorage.getLong(RebootEscrowManager.REBOOT_ESCROW_KEY_ARMED_TIMESTAMP,
+                -1, USER_SYSTEM), -1);
     }
 
     @Test
@@ -451,7 +490,9 @@
         // pretend reboot happens here
         when(mInjected.getBootCount()).thenReturn(1);
         ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
-        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
+        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
+                eq(0) /* error code */, eq(2) /* Server based */, eq(1) /* attempt count */,
+                anyInt(), eq(0) /* vbmeta status */, anyInt());
 
         when(mServiceConnection.unwrap(any(), anyLong()))
                 .thenAnswer(invocation -> invocation.getArgument(0));
@@ -462,6 +503,84 @@
     }
 
     @Test
+    public void loadRebootEscrowDataIfAvailable_ServerBasedRemoteException_Failure()
+            throws Exception {
+        setServerBasedRebootEscrowProvider();
+
+        when(mInjected.getBootCount()).thenReturn(0);
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        clearInvocations(mServiceConnection);
+        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+        // Use x -> x for both wrap & unwrap functions.
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        assertTrue(mService.armRebootEscrowIfNeeded());
+        verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+        // pretend reboot happens here
+        when(mInjected.getBootCount()).thenReturn(1);
+        ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+        ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
+        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
+                metricsErrorCodeCaptor.capture(), eq(2) /* Server based */,
+                eq(1) /* attempt count */, anyInt(), eq(0) /* vbmeta status */, anyInt());
+
+        when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(RemoteException.class);
+        mService.loadRebootEscrowDataIfAvailable(null);
+        verify(mServiceConnection).unwrap(any(), anyLong());
+        assertFalse(metricsSuccessCaptor.getValue());
+        assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY),
+                metricsErrorCodeCaptor.getValue());
+    }
+
+    @Test
+    public void loadRebootEscrowDataIfAvailable_ServerBasedIoError_RetryFailure() throws Exception {
+        setServerBasedRebootEscrowProvider();
+
+        when(mInjected.getBootCount()).thenReturn(0);
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        clearInvocations(mServiceConnection);
+        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+        // Use x -> x for both wrap & unwrap functions.
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        assertTrue(mService.armRebootEscrowIfNeeded());
+        verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+        // pretend reboot happens here
+        when(mInjected.getBootCount()).thenReturn(1);
+        ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+        ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
+        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
+                metricsErrorCodeCaptor.capture(), eq(2) /* Server based */,
+                eq(2) /* attempt count */, anyInt(), eq(0) /* vbmeta status */, anyInt());
+        when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(IOException.class);
+
+        HandlerThread thread = new HandlerThread("RebootEscrowManagerTest");
+        thread.start();
+        mService.loadRebootEscrowDataIfAvailable(new Handler(thread.getLooper()));
+        // Sleep 5s for the retry to complete
+        Thread.sleep(5 * 1000);
+        assertFalse(metricsSuccessCaptor.getValue());
+        assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_RETRY_COUNT_EXHAUSTED),
+                metricsErrorCodeCaptor.getValue());
+    }
+
+    @Test
     public void loadRebootEscrowDataIfAvailable_ServerBased_RetrySuccess() throws Exception {
         setServerBasedRebootEscrowProvider();
 
@@ -485,7 +604,8 @@
         // pretend reboot happens here
         when(mInjected.getBootCount()).thenReturn(1);
         ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
-        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
+        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
+                anyInt(), anyInt(), eq(2) /* attempt count */, anyInt(), anyInt(), anyInt());
 
         when(mServiceConnection.unwrap(any(), anyLong()))
                 .thenThrow(new IOException())
@@ -528,7 +648,8 @@
 
         mService.loadRebootEscrowDataIfAvailable(null);
         verify(mRebootEscrow).retrieveKey();
-        verify(mInjected, never()).reportMetric(anyBoolean());
+        verify(mInjected, never()).reportMetric(anyBoolean(), anyInt(), anyInt(), anyInt(),
+                anyInt(), anyInt(), anyInt());
     }
 
     @Test
@@ -554,7 +675,8 @@
         when(mRebootEscrow.retrieveKey()).thenReturn(new byte[32]);
 
         mService.loadRebootEscrowDataIfAvailable(null);
-        verify(mInjected, never()).reportMetric(anyBoolean());
+        verify(mInjected, never()).reportMetric(anyBoolean(), anyInt(), anyInt(), anyInt(),
+                anyInt(), anyInt(), anyInt());
     }
 
     @Test
@@ -587,8 +709,14 @@
         when(mInjected.getBootCount()).thenReturn(10);
         when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue());
 
+        // Trigger a vbmeta digest mismatch
+        mStorage.setString(RebootEscrowManager.REBOOT_ESCROW_KEY_VBMETA_DIGEST,
+                "non sense value", USER_SYSTEM);
         mService.loadRebootEscrowDataIfAvailable(null);
-        verify(mInjected).reportMetric(eq(true));
+        verify(mInjected).reportMetric(eq(true), eq(0) /* error code */, eq(1) /* HAL based */,
+                eq(1) /* attempt count */, anyInt(), eq(2) /* vbmeta status */, anyInt());
+        assertEquals(mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_KEY_VBMETA_DIGEST,
+                "", USER_SYSTEM), "");
     }
 
     @Test
@@ -615,10 +743,17 @@
 
         when(mInjected.getBootCount()).thenReturn(1);
         ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
-        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
-        when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> new byte[32]);
+        ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
+        // Return a null escrow key
+        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
+                metricsErrorCodeCaptor.capture(), eq(1) /* HAL based */,
+                eq(1) /* attempt count */, anyInt(), anyInt(), anyInt());
+
+        when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> null);
         mService.loadRebootEscrowDataIfAvailable(null);
         verify(mRebootEscrow).retrieveKey();
         assertFalse(metricsSuccessCaptor.getValue());
+        assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY),
+                metricsErrorCodeCaptor.getValue());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index a38745f..d9af51f 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -42,7 +42,6 @@
 
 import android.content.Context;
 import android.os.FileUtils;
-import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.recovery.KeyChainSnapshot;
@@ -109,7 +108,7 @@
     private RecoverySnapshotStorage mRecoverySnapshotStorage;
     private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
     private File mDatabaseFile;
-    private AndroidKeyStoreSecretKey mWrappingKey;
+    private SecretKey mWrappingKey;
     private PlatformEncryptionKey mEncryptKey;
 
     private KeySyncTask mKeySyncTask;
@@ -848,7 +847,7 @@
         return keyGenerator.generateKey();
     }
 
-    private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+    private SecretKey generateAndroidKeyStoreKey() throws Exception {
         KeyGenerator keyGenerator = KeyGenerator.getInstance(
                 KEY_ALGORITHM,
                 ANDROID_KEY_STORE_PROVIDER);
@@ -857,7 +856,7 @@
                 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                 .build());
-        return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+        return keyGenerator.generateKey();
     }
 
     private static byte[] utf8Bytes(String s) {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
index 670bd81..fd4fe56 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
@@ -34,7 +34,6 @@
 import android.content.Context;
 import android.os.RemoteException;
 import android.security.GateKeeper;
-import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeyProtection;
@@ -61,6 +60,7 @@
 import java.util.List;
 
 import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -586,7 +586,7 @@
         return (KeyProtection) mProtectionParameterCaptor.getValue();
     }
 
-    private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+    private SecretKey generateAndroidKeyStoreKey() throws Exception {
         KeyGenerator keyGenerator = KeyGenerator.getInstance(
                 KEY_ALGORITHM,
                 ANDROID_KEY_STORE_PROVIDER);
@@ -595,7 +595,7 @@
                 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                 .build());
-        return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+        return keyGenerator.generateKey();
     }
 
     class PlatformKeyManagerTestable extends PlatformKeyManager {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
index c295177..6413026 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
@@ -23,7 +23,6 @@
 import static org.junit.Assert.assertNull;
 
 import android.content.Context;
-import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 
@@ -45,6 +44,7 @@
 
 import javax.crypto.Cipher;
 import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
 import javax.crypto.spec.GCMParameterSpec;
 
 @SmallTest
@@ -77,7 +77,7 @@
         mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
         mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
 
-        AndroidKeyStoreSecretKey platformKey = generatePlatformKey();
+        SecretKey platformKey = generatePlatformKey();
         mPlatformKey = new PlatformEncryptionKey(TEST_GENERATION_ID, platformKey);
         mDecryptKey = new PlatformDecryptionKey(TEST_GENERATION_ID, platformKey);
         mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mRecoverableKeyStoreDb);
@@ -168,7 +168,7 @@
         assertArrayEquals(rawMaterial, unwrappedMaterial);
     }
 
-    private AndroidKeyStoreSecretKey generatePlatformKey() throws Exception {
+    private SecretKey generatePlatformKey() throws Exception {
         KeyGenerator keyGenerator = KeyGenerator.getInstance(
                 KEY_ALGORITHM,
                 ANDROID_KEY_STORE_PROVIDER);
@@ -177,7 +177,7 @@
                     .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                     .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                     .build());
-        return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+        return keyGenerator.generateKey();
     }
 
     private static byte[] randomBytes(int n) {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index ac74470..f4e74ba 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -45,7 +45,6 @@
 import android.os.Binder;
 import android.os.ServiceSpecificException;
 import android.os.UserHandle;
-import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.recovery.KeyChainProtectionParams;
@@ -1311,7 +1310,7 @@
         mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
     }
 
-    private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+    private SecretKey generateAndroidKeyStoreKey() throws Exception {
         KeyGenerator keyGenerator = KeyGenerator.getInstance(
                 KEY_ALGORITHM,
                 ANDROID_KEY_STORE_PROVIDER);
@@ -1320,6 +1319,6 @@
                 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                 .build());
-        return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+        return keyGenerator.generateKey();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
index 9813ab7..60052f7 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
@@ -21,7 +21,6 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 import android.util.Pair;
@@ -117,7 +116,7 @@
     @Test
     public void decryptWrappedKeys_decryptsWrappedKeys_nullMetadata() throws Exception {
         String alias = "karlin";
-        AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+        SecretKey platformKey = generateAndroidKeyStoreKey();
         SecretKey appKey = generateKey();
         WrappedKey wrappedKey = WrappedKey.fromSecretKey(
                 new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey, NULL_METADATA);
@@ -136,7 +135,7 @@
     @Test
     public void decryptWrappedKeys_decryptsWrappedKeys_nonNullMetadata() throws Exception {
         String alias = "karlin";
-        AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+        SecretKey platformKey = generateAndroidKeyStoreKey();
         SecretKey appKey = generateKey();
         WrappedKey wrappedKey = WrappedKey.fromSecretKey(
                 new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey, NON_NULL_METADATA);
@@ -155,7 +154,7 @@
     @Test
     public void decryptWrappedKeys_doesNotDieIfSomeKeysAreUnwrappable() throws Exception {
         String alias = "karlin";
-        AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+        SecretKey platformKey = generateAndroidKeyStoreKey();
         SecretKey appKey = generateKey();
         WrappedKey wrappedKey = WrappedKey.fromSecretKey(
                 new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey, NULL_METADATA);
@@ -171,7 +170,7 @@
 
     @Test
     public void decryptWrappedKeys_throwsIfPlatformKeyGenerationIdDoesNotMatch() throws Exception {
-        AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+        SecretKey platformKey = generateAndroidKeyStoreKey();
         WrappedKey wrappedKey = WrappedKey.fromSecretKey(
                 new PlatformEncryptionKey(GENERATION_ID, platformKey), generateKey(),
                 /*metadata=*/ null);
@@ -197,7 +196,7 @@
         return keyGenerator.generateKey();
     }
 
-    private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+    private SecretKey generateAndroidKeyStoreKey() throws Exception {
         KeyGenerator keyGenerator = KeyGenerator.getInstance(
                 KEY_ALGORITHM,
                 ANDROID_KEY_STORE_PROVIDER);
@@ -207,6 +206,6 @@
                 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                 .build());
-        return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+        return keyGenerator.generateKey();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
new file mode 100644
index 0000000..13d75a7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
@@ -0,0 +1,292 @@
+/*
+ * 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.pm.dex;
+
+import static org.mockito.Mockito.inOrder;
+
+import com.android.internal.art.ArtStatsLog;
+import com.android.server.pm.dex.ArtStatsLogUtils.ArtStatsLogger;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.InOrder;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * Unit tests for {@link com.android.server.pm.dex.ArtStatsLogUtils}.
+ *
+ * Run with "atest ArtStatsLogUtilsTest".
+ */
+@RunWith(JUnit4.class)
+public final class ArtStatsLogUtilsTest {
+    private static final String TAG = ArtStatsLogUtilsTest.class.getSimpleName();
+    private static final String COMPILER_FILTER = "space-profile";
+    private static final String PROFILE_DEX_METADATA = "primary.prof";
+    private static final String VDEX_DEX_METADATA = "primary.vdex";
+    private static final byte[] DEX_CONTENT = "dexData".getBytes();
+    private static final int COMPILATION_REASON = 1;
+    private static final int RESULT_CODE = 222;
+    private static final int UID = 111;
+    private static final long COMPILE_TIME = 333L;
+    private static final long SESSION_ID = 444L;
+
+    @Mock
+    ArtStatsLogger mockLogger;
+
+    private static Path TEST_DIR;
+    private static Path DEX;
+    private static Path NON_DEX;
+
+    @BeforeClass
+    public static void setUpAll() throws IOException {
+        TEST_DIR = Files.createTempDirectory(null);
+        DEX = Files.createFile(TEST_DIR.resolve("classes.dex"));
+        NON_DEX = Files.createFile(TEST_DIR.resolve("test.dex"));
+        Files.write(DEX, DEX_CONTENT);
+        Files.write(NON_DEX, "empty".getBytes());
+    }
+
+    @AfterClass
+    public static void tearnDownAll() {
+        deleteSliently(DEX);
+        deleteSliently(NON_DEX);
+        deleteSliently(TEST_DIR);
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testProfileAndVdexDexMetadata() throws IOException {
+        // Setup
+        Path dexMetadataPath = null;
+        Path apk = null;
+        try {
+            dexMetadataPath = createDexMetadata(PROFILE_DEX_METADATA, VDEX_DEX_METADATA);
+            apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath);
+
+            // Act
+            ArtStatsLogUtils.writeStatsLog(
+                    mockLogger,
+                    SESSION_ID,
+                    apk.toString(),
+                    COMPILER_FILTER,
+                    UID,
+                    COMPILE_TIME,
+                    dexMetadataPath.toString(),
+                    COMPILATION_REASON,
+                    RESULT_CODE);
+
+            // Assert
+            verifyWrites(ArtStatsLog.
+                ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE_AND_VDEX);
+        } finally {
+            deleteSliently(dexMetadataPath);
+            deleteSliently(apk);
+        }
+    }
+
+    @Test
+    public void testProfileOnlyDexMetadata() throws IOException {
+        // Setup
+        Path dexMetadataPath = null;
+        Path apk = null;
+        try {
+            dexMetadataPath = createDexMetadata(PROFILE_DEX_METADATA);
+            apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath);
+
+            // Act
+            ArtStatsLogUtils.writeStatsLog(
+                    mockLogger,
+                    SESSION_ID,
+                    apk.toString(),
+                    COMPILER_FILTER,
+                    UID,
+                    COMPILE_TIME,
+                    dexMetadataPath.toString(),
+                    COMPILATION_REASON,
+                    RESULT_CODE);
+
+            // Assert
+            verifyWrites(ArtStatsLog.
+                ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE);
+        } finally {
+            deleteSliently(dexMetadataPath);
+            deleteSliently(apk);
+        }
+    }
+
+    @Test
+    public void testVdexOnlyDexMetadata() throws IOException {
+        // Setup
+        Path dexMetadataPath = null;
+        Path apk = null;
+        try {
+            dexMetadataPath = createDexMetadata(VDEX_DEX_METADATA);
+            apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath);
+
+            // Act
+            ArtStatsLogUtils.writeStatsLog(
+                    mockLogger,
+                    SESSION_ID,
+                    apk.toString(),
+                    COMPILER_FILTER,
+                    UID,
+                    COMPILE_TIME,
+                    dexMetadataPath.toString(),
+                    COMPILATION_REASON,
+                    RESULT_CODE);
+
+            // Assert
+            verifyWrites(ArtStatsLog.
+                ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_VDEX);
+        } finally {
+            deleteSliently(dexMetadataPath);
+            deleteSliently(apk);
+        }
+    }
+
+    @Test
+    public void testNoneDexMetadata() throws IOException {
+        // Setup
+        Path apk = null;
+        try {
+            apk = zipFiles(".apk", DEX, NON_DEX);
+
+            // Act
+            ArtStatsLogUtils.writeStatsLog(
+                    mockLogger,
+                    SESSION_ID,
+                    apk.toString(),
+                    COMPILER_FILTER,
+                    UID,
+                    COMPILE_TIME,
+                    /*dexMetadataPath=*/ null,
+                    COMPILATION_REASON,
+                    RESULT_CODE);
+
+            // Assert
+            verifyWrites(ArtStatsLog.
+                ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_NONE);
+        } finally {
+            deleteSliently(apk);
+        }
+    }
+
+    @Test
+    public void testUnKnownDexMetadata() throws IOException {
+        // Setup
+        Path dexMetadataPath = null;
+        Path apk = null;
+        try {
+            dexMetadataPath = createDexMetadata("unknown");
+            apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath);
+
+            // Act
+            ArtStatsLogUtils.writeStatsLog(
+                    mockLogger,
+                    SESSION_ID,
+                    apk.toString(),
+                    COMPILER_FILTER,
+                    UID,
+                    COMPILE_TIME,
+                    dexMetadataPath.toString(),
+                    COMPILATION_REASON,
+                    RESULT_CODE);
+
+            // Assert
+            verifyWrites(ArtStatsLog.
+                ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_UNKNOWN);
+        } finally {
+            deleteSliently(dexMetadataPath);
+            deleteSliently(apk);
+        }
+    }
+
+    private void verifyWrites(int dexMetadataType) {
+        InOrder inorder = inOrder(mockLogger);
+        inorder.verify(mockLogger).write(
+                SESSION_ID, UID,
+                COMPILATION_REASON,
+                COMPILER_FILTER,
+                ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_RESULT_CODE,
+                RESULT_CODE,
+                dexMetadataType);
+        inorder.verify(mockLogger).write(
+                SESSION_ID,
+                UID,
+                COMPILATION_REASON,
+                COMPILER_FILTER,
+                ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME,
+                COMPILE_TIME,
+                dexMetadataType);
+    }
+
+    private Path zipFiles(String suffix, Path... files) throws IOException {
+        Path zipFile = Files.createTempFile(null, suffix);
+        try (final OutputStream os = Files.newOutputStream(zipFile)) {
+            try (final ZipOutputStream zos = new ZipOutputStream(os)) {
+                for (Path file : files) {
+                    ZipEntry zipEntry = new ZipEntry(file.getFileName().toString());
+                    zos.putNextEntry(zipEntry);
+                    zos.write(Files.readAllBytes(file));
+                    zos.closeEntry();
+                }
+            }
+        }
+        return zipFile;
+    }
+
+    private Path createDexMetadata(String... entryNames) throws IOException {
+        Path zipFile = Files.createTempFile(null, ".dm");
+        try (final OutputStream os = Files.newOutputStream(zipFile)) {
+            try (final ZipOutputStream zos = new ZipOutputStream(os)) {
+                for (String entryName : entryNames) {
+                    ZipEntry zipEntry = new ZipEntry(entryName);
+                    zos.putNextEntry(zipEntry);
+                    zos.write(entryName.getBytes());
+                    zos.closeEntry();
+                }
+            }
+        }
+        return zipFile;
+    }
+
+    private static void deleteSliently(Path file) {
+        if (file != null) {
+            try {
+                Files.deleteIfExists(file);
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
index 9b8a2a8..7903a90 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
@@ -18,9 +18,11 @@
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertThat;
+import static org.mockito.AdditionalMatchers.not;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
@@ -70,6 +72,8 @@
     private FileWriter mUncryptUpdateFileWriter;
     private LockSettingsInternal mLockSettingsInternal;
     private IBootControl mIBootControl;
+    private RecoverySystemServiceTestable.IMetricsReporter mMetricsReporter;
+    private RecoverySystemService.PreferencesManager mSharedPreferences;
 
     private static final String FAKE_OTA_PACKAGE_NAME = "fake.ota.package";
     private static final String FAKE_OTHER_PACKAGE_NAME = "fake.other.package";
@@ -94,9 +98,12 @@
         when(mIBootControl.getCurrentSlot()).thenReturn(0);
         when(mIBootControl.getActiveBootSlot()).thenReturn(1);
 
+        mMetricsReporter = mock(RecoverySystemServiceTestable.IMetricsReporter.class);
+        mSharedPreferences = mock(RecoverySystemService.PreferencesManager.class);
+
         mRecoverySystemService = new RecoverySystemServiceTestable(mContext, mSystemProperties,
                 powerManager, mUncryptUpdateFileWriter, mUncryptSocket, mLockSettingsInternal,
-                mIBootControl);
+                mIBootControl, mMetricsReporter, mSharedPreferences);
     }
 
     @Test
@@ -227,12 +234,35 @@
     }
 
     @Test
+    public void requestLskf_reportMetrics() throws Exception {
+        IntentSender intentSender = mock(IntentSender.class);
+        assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, intentSender),
+                is(true));
+        verify(mMetricsReporter).reportRebootEscrowPreparationMetrics(
+                eq(1000), eq(0) /* need preparation */, eq(1) /* client count */);
+        verify(mSharedPreferences).putLong(eq(FAKE_OTA_PACKAGE_NAME
+                + RecoverySystemService.REQUEST_LSKF_TIMESTAMP_PREF_SUFFIX), eq(100_000L));
+    }
+
+
+    @Test
     public void requestLskf_success() throws Exception {
         IntentSender intentSender = mock(IntentSender.class);
         assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, intentSender),
                 is(true));
+
+        when(mSharedPreferences.getLong(eq(FAKE_OTA_PACKAGE_NAME
+                + RecoverySystemService.REQUEST_LSKF_TIMESTAMP_PREF_SUFFIX), anyLong()))
+                .thenReturn(200_000L).thenReturn(5000L);
+        mRecoverySystemService.onPreparedForReboot(true);
+        verify(mMetricsReporter).reportRebootEscrowLskfCapturedMetrics(
+                eq(1000), eq(1) /* client count */,
+                eq(-1) /* invalid duration */);
+
         mRecoverySystemService.onPreparedForReboot(true);
         verify(intentSender).sendIntent(any(), anyInt(), any(), any(), any());
+        verify(mMetricsReporter).reportRebootEscrowLskfCapturedMetrics(
+                eq(1000), eq(1) /* client count */, eq(95) /* duration */);
     }
 
     @Test
@@ -255,6 +285,8 @@
         assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, intentSender),
                 is(true));
         verify(intentSender, never()).sendIntent(any(), anyInt(), any(), any(), any());
+        verify(mMetricsReporter, never()).reportRebootEscrowLskfCapturedMetrics(
+                anyInt(), anyInt(), anyInt());
     }
 
     @Test
@@ -334,9 +366,19 @@
     public void rebootWithLskf_Success() throws Exception {
         assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, null), is(true));
         mRecoverySystemService.onPreparedForReboot(true);
+
+        when(mSharedPreferences.getInt(eq(FAKE_OTA_PACKAGE_NAME
+                + RecoverySystemService.REQUEST_LSKF_COUNT_PREF_SUFFIX), anyInt())).thenReturn(2);
+        when(mSharedPreferences.getInt(eq(RecoverySystemService.LSKF_CAPTURED_COUNT_PREF),
+                anyInt())).thenReturn(3);
+        when(mSharedPreferences.getLong(eq(RecoverySystemService.LSKF_CAPTURED_TIMESTAMP_PREF),
+                anyLong())).thenReturn(40_000L);
         assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", true),
                 is(true));
         verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
+        verify(mMetricsReporter).reportRebootEscrowRebootMetrics(eq(0), eq(1000),
+                eq(1) /* client count */, eq(2) /* request count */, eq(true) /* slot switch */,
+                anyBoolean(), eq(60) /* duration */, eq(3) /* lskf capture count */);
     }
 
 
@@ -373,6 +415,26 @@
         verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
     }
 
+    @Test
+    public void rebootWithLskf_multiClient_success_reportMetrics() throws Exception {
+        assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, null), is(true));
+        assertThat(mRecoverySystemService.requestLskf(FAKE_OTHER_PACKAGE_NAME, null), is(true));
+        mRecoverySystemService.onPreparedForReboot(true);
+
+        when(mSharedPreferences.getInt(eq(FAKE_OTA_PACKAGE_NAME
+                + RecoverySystemService.REQUEST_LSKF_COUNT_PREF_SUFFIX), anyInt())).thenReturn(2);
+        when(mSharedPreferences.getInt(eq(RecoverySystemService.LSKF_CAPTURED_COUNT_PREF),
+                anyInt())).thenReturn(1);
+        when(mSharedPreferences.getLong(eq(RecoverySystemService.LSKF_CAPTURED_TIMESTAMP_PREF),
+                anyLong())).thenReturn(60_000L);
+
+        assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", true),
+                is(true));
+        verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
+        verify(mMetricsReporter).reportRebootEscrowRebootMetrics(eq(0), eq(1000),
+                eq(2) /* client count */, eq(2) /* request count */, eq(true) /* slot switch */,
+                anyBoolean(), eq(40), eq(1) /* lskf capture count */);
+    }
 
     @Test
     public void rebootWithLskf_multiClient_ClientBSuccess() throws Exception {
@@ -380,16 +442,30 @@
         mRecoverySystemService.onPreparedForReboot(true);
         assertThat(mRecoverySystemService.requestLskf(FAKE_OTHER_PACKAGE_NAME, null), is(true));
 
+        when(mSharedPreferences.getInt(eq(FAKE_OTHER_PACKAGE_NAME
+                + RecoverySystemService.REQUEST_LSKF_COUNT_PREF_SUFFIX), anyInt())).thenReturn(2);
+        when(mSharedPreferences.getInt(eq(RecoverySystemService.LSKF_CAPTURED_COUNT_PREF),
+                anyInt())).thenReturn(1);
+        when(mSharedPreferences.getLong(eq(RecoverySystemService.LSKF_CAPTURED_TIMESTAMP_PREF),
+                anyLong())).thenReturn(60_000L);
+
         assertThat(mRecoverySystemService.clearLskf(FAKE_OTA_PACKAGE_NAME), is(true));
         assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true),
                 is(false));
         verifyNoMoreInteractions(mIPowerManager);
+        verify(mMetricsReporter).reportRebootEscrowRebootMetrics(not(eq(0)), eq(1000),
+                eq(1) /* client count */, anyInt() /* request count */, eq(true) /* slot switch */,
+                anyBoolean(), eq(40), eq(1)/* lskf capture count */);
 
         assertThat(mRecoverySystemService.requestLskf(FAKE_OTHER_PACKAGE_NAME, null), is(true));
         assertThat(
                 mRecoverySystemService.rebootWithLskf(FAKE_OTHER_PACKAGE_NAME, "ab-update", true),
                 is(true));
         verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
+
+        verify(mMetricsReporter).reportRebootEscrowRebootMetrics((eq(0)), eq(2000),
+                eq(1) /* client count */, eq(2) /* request count */, eq(true) /* slot switch */,
+                anyBoolean(), eq(40), eq(1) /* lskf capture count */);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java
index 0727e5a..27e953f 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java
@@ -32,11 +32,14 @@
         private final UncryptSocket mUncryptSocket;
         private final LockSettingsInternal mLockSettingsInternal;
         private final IBootControl mIBootControl;
+        private final IMetricsReporter mIMetricsReporter;
+        private final RecoverySystemService.PreferencesManager mSharedPreferences;
 
         MockInjector(Context context, FakeSystemProperties systemProperties,
                 PowerManager powerManager, FileWriter uncryptPackageFileWriter,
                 UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal,
-                IBootControl bootControl) {
+                IBootControl bootControl, IMetricsReporter metricsReporter,
+                RecoverySystemService.PreferencesManager preferences) {
             super(context);
             mSystemProperties = systemProperties;
             mPowerManager = powerManager;
@@ -44,6 +47,8 @@
             mUncryptSocket = uncryptSocket;
             mLockSettingsInternal = lockSettingsInternal;
             mIBootControl = bootControl;
+            mIMetricsReporter = metricsReporter;
+            mSharedPreferences = preferences;
         }
 
         @Override
@@ -94,14 +99,58 @@
         public IBootControl getBootControl() {
             return mIBootControl;
         }
+        @Override
+        public int getUidFromPackageName(String packageName) {
+            if ("fake.ota.package".equals(packageName)) {
+                return 1000;
+            }
+            if ("fake.other.package".equals(packageName)) {
+                return 2000;
+            }
+            return 3000;
+        }
+
+        @Override
+        public void reportRebootEscrowPreparationMetrics(int uid, int requestResult,
+                int requestedClientCount) {
+            mIMetricsReporter.reportRebootEscrowPreparationMetrics(uid, requestResult,
+                    requestedClientCount);
+        }
+
+        @Override
+        public void reportRebootEscrowLskfCapturedMetrics(int uid, int requestedClientCount,
+                int requestedToLskfCapturedDurationInSeconds) {
+            mIMetricsReporter.reportRebootEscrowLskfCapturedMetrics(uid, requestedClientCount,
+                    requestedToLskfCapturedDurationInSeconds);
+        }
+
+        @Override
+        public void reportRebootEscrowRebootMetrics(int errorCode, int uid, int preparedClientCount,
+                int requestCount, boolean slotSwitch, boolean serverBased,
+                int lskfCapturedToRebootDurationInSeconds, int lskfCapturedCounts) {
+            mIMetricsReporter.reportRebootEscrowRebootMetrics(errorCode, uid, preparedClientCount,
+                    requestCount, slotSwitch, serverBased, lskfCapturedToRebootDurationInSeconds,
+                    lskfCapturedCounts);
+        }
+
+        @Override
+        public long getCurrentTimeMillis() {
+            return 100_000;
+        }
+
+        @Override
+        public RecoverySystemService.PreferencesManager getMetricsPrefs() {
+            return mSharedPreferences;
+        }
     }
 
     RecoverySystemServiceTestable(Context context, FakeSystemProperties systemProperties,
             PowerManager powerManager, FileWriter uncryptPackageFileWriter,
             UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal,
-            IBootControl bootControl) {
+            IBootControl bootControl, IMetricsReporter metricsReporter,
+            RecoverySystemService.PreferencesManager preferences) {
         super(new MockInjector(context, systemProperties, powerManager, uncryptPackageFileWriter,
-                uncryptSocket, lockSettingsInternal, bootControl));
+                uncryptSocket, lockSettingsInternal, bootControl, metricsReporter, preferences));
     }
 
     public static class FakeSystemProperties {
@@ -131,4 +180,16 @@
             return mCtlStart;
         }
     }
+
+    public interface IMetricsReporter {
+        void reportRebootEscrowPreparationMetrics(int uid, int requestResult,
+                int requestedClientCount);
+
+        void reportRebootEscrowLskfCapturedMetrics(int uid, int requestedClientCount,
+                int requestedToLskfCapturedDurationInSeconds);
+
+        void reportRebootEscrowRebootMetrics(int errorCode, int uid, int preparedClientCount,
+                int requestCount, boolean slotSwitch, boolean serverBased,
+                int lskfCapturedToRebootDurationInSeconds, int lskfCapturedCounts);
+    }
 }
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/OWNERS b/services/tests/servicestests/test-apps/SimpleServiceTestApp/OWNERS
new file mode 100644
index 0000000..72c0a9e
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/am/OWNERS
diff --git a/services/tests/servicestests/utils-mockito/com/android/server/testutils/OWNERS b/services/tests/servicestests/utils-mockito/com/android/server/testutils/OWNERS
new file mode 100644
index 0000000..d825dfd
--- /dev/null
+++ b/services/tests/servicestests/utils-mockito/com/android/server/testutils/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/pm/OWNERS
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 4259831..ee89e1c 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -833,10 +833,10 @@
 
     @Test
     public void testDefaultAssistant_overrideDefault() {
-        final int userId = 0;
+        final int userId = mContext.getUserId();
         final String testComponent = "package/class";
         final List<UserInfo> userInfos = new ArrayList<>();
-        userInfos.add(new UserInfo(0, "", 0));
+        userInfos.add(new UserInfo(userId, "", 0));
         final ArraySet<ComponentName> validAssistants = new ArraySet<>();
         validAssistants.add(ComponentName.unflattenFromString(testComponent));
         when(mActivityManager.isLowRamDevice()).thenReturn(false);
@@ -2393,7 +2393,7 @@
                 .thenReturn(mTestNotificationChannel);
 
         reset(mListeners);
-        mBinderService.updateNotificationChannelForPackage(PKG, 0, mTestNotificationChannel);
+        mBinderService.updateNotificationChannelForPackage(PKG, mUid, mTestNotificationChannel);
         verify(mListeners, times(1)).notifyNotificationChannelChanged(eq(PKG),
                 eq(Process.myUserHandle()), eq(mTestNotificationChannel),
                 eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
@@ -2929,7 +2929,7 @@
 
     @Test
     public void testSetListenerAccessForUser() throws Exception {
-        UserHandle user = UserHandle.of(10);
+        UserHandle user = UserHandle.of(mContext.getUserId() + 10);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
         mBinderService.setNotificationListenerAccessGrantedForUser(c, user.getIdentifier(), true);
 
@@ -2945,20 +2945,20 @@
 
     @Test
     public void testSetAssistantAccessForUser() throws Exception {
-        UserHandle user = UserHandle.of(10);
-        List<UserInfo> uis = new ArrayList<>();
         UserInfo ui = new UserInfo();
-        ui.id = 10;
+        ui.id = mContext.getUserId() + 10;
+        UserHandle user = UserHandle.of(ui.id);
+        List<UserInfo> uis = new ArrayList<>();
         uis.add(ui);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
-        when(mUm.getEnabledProfiles(10)).thenReturn(uis);
+        when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
 
         mBinderService.setNotificationAssistantAccessGrantedForUser(c, user.getIdentifier(), true);
 
         verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any());
         verify(mAssistants, times(1)).setPackageOrComponentEnabled(
                 c.flattenToString(), user.getIdentifier(), true, true);
-        verify(mAssistants).setUserSet(10, true);
+        verify(mAssistants).setUserSet(ui.id, true);
         verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
                 c.flattenToString(), user.getIdentifier(), false, true);
         verify(mListeners, never()).setPackageOrComponentEnabled(
@@ -2967,7 +2967,7 @@
 
     @Test
     public void testGetAssistantAllowedForUser() throws Exception {
-        UserHandle user = UserHandle.of(10);
+        UserHandle user = UserHandle.of(mContext.getUserId() + 10);
         try {
             mBinderService.getAllowedNotificationAssistantForUser(user.getIdentifier());
         } catch (IllegalStateException e) {
@@ -2987,12 +2987,12 @@
                 throw e;
             }
         }
-        verify(mAssistants, times(1)).getAllowedComponents(0);
+        verify(mAssistants, times(1)).getAllowedComponents(mContext.getUserId());
     }
 
     @Test
     public void testSetDndAccessForUser() throws Exception {
-        UserHandle user = UserHandle.of(10);
+        UserHandle user = UserHandle.of(mContext.getUserId() + 10);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
         mBinderService.setNotificationPolicyAccessGrantedForUser(
                 c.getPackageName(), user.getIdentifier(), true);
@@ -3012,9 +3012,9 @@
         mBinderService.setNotificationListenerAccessGranted(c, true);
 
         verify(mListeners, times(1)).setPackageOrComponentEnabled(
-                c.flattenToString(), 0, true, true);
+                c.flattenToString(), mContext.getUserId(), true, true);
         verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
-                c.flattenToString(), 0, false, true);
+                c.flattenToString(), mContext.getUserId(), false, true);
         verify(mAssistants, never()).setPackageOrComponentEnabled(
                 any(), anyInt(), anyBoolean(), anyBoolean());
     }
@@ -3023,7 +3023,7 @@
     public void testSetAssistantAccess() throws Exception {
         List<UserInfo> uis = new ArrayList<>();
         UserInfo ui = new UserInfo();
-        ui.id = 0;
+        ui.id = mContext.getUserId();
         uis.add(ui);
         when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
@@ -3031,9 +3031,9 @@
         mBinderService.setNotificationAssistantAccessGranted(c, true);
 
         verify(mAssistants, times(1)).setPackageOrComponentEnabled(
-                c.flattenToString(), 0, true, true);
+                c.flattenToString(), ui.id, true, true);
         verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
-                c.flattenToString(), 0, false, true);
+                c.flattenToString(), ui.id, false, true);
         verify(mListeners, never()).setPackageOrComponentEnabled(
                 any(), anyInt(), anyBoolean(), anyBoolean());
     }
@@ -3042,10 +3042,10 @@
     public void testSetAssistantAccess_multiProfile() throws Exception {
         List<UserInfo> uis = new ArrayList<>();
         UserInfo ui = new UserInfo();
-        ui.id = 0;
+        ui.id = mContext.getUserId();
         uis.add(ui);
         UserInfo ui10 = new UserInfo();
-        ui10.id = 10;
+        ui10.id = mContext.getUserId() + 10;
         uis.add(ui10);
         when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
@@ -3053,13 +3053,13 @@
         mBinderService.setNotificationAssistantAccessGranted(c, true);
 
         verify(mAssistants, times(1)).setPackageOrComponentEnabled(
-                c.flattenToString(), 0, true, true);
+                c.flattenToString(), ui.id, true, true);
         verify(mAssistants, times(1)).setPackageOrComponentEnabled(
-                c.flattenToString(), 10, true, true);
+                c.flattenToString(), ui10.id, true, true);
         verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
-                c.flattenToString(), 0, false, true);
+                c.flattenToString(), ui.id, false, true);
         verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
-                c.flattenToString(), 10, false, true);
+                c.flattenToString(), ui10.id, false, true);
         verify(mListeners, never()).setPackageOrComponentEnabled(
                 any(), anyInt(), anyBoolean(), anyBoolean());
     }
@@ -3072,16 +3072,16 @@
         when(mAssistants.getAllowedComponents(anyInt())).thenReturn(componentList);
         List<UserInfo> uis = new ArrayList<>();
         UserInfo ui = new UserInfo();
-        ui.id = 0;
+        ui.id = mContext.getUserId();
         uis.add(ui);
         when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
 
         mBinderService.setNotificationAssistantAccessGranted(null, true);
 
         verify(mAssistants, times(1)).setPackageOrComponentEnabled(
-                c.flattenToString(), 0, true, false);
+                c.flattenToString(), ui.id, true, false);
         verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
-                c.flattenToString(), 0, false,  false);
+                c.flattenToString(), ui.id, false,  false);
         verify(mListeners, never()).setPackageOrComponentEnabled(
                 any(), anyInt(), anyBoolean(), anyBoolean());
     }
@@ -3090,21 +3090,21 @@
     public void testSetAssistantAccessForUser_nullWithAllowedAssistant() throws Exception {
         List<UserInfo> uis = new ArrayList<>();
         UserInfo ui = new UserInfo();
-        ui.id = 10;
+        ui.id = mContext.getUserId() + 10;
         uis.add(ui);
         UserHandle user = ui.getUserHandle();
         ArrayList<ComponentName> componentList = new ArrayList<>();
         ComponentName c = ComponentName.unflattenFromString("package/Component");
         componentList.add(c);
         when(mAssistants.getAllowedComponents(anyInt())).thenReturn(componentList);
-        when(mUm.getEnabledProfiles(10)).thenReturn(uis);
+        when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
 
         mBinderService.setNotificationAssistantAccessGrantedForUser(
                 null, user.getIdentifier(), true);
 
         verify(mAssistants, times(1)).setPackageOrComponentEnabled(
                 c.flattenToString(), user.getIdentifier(), true, false);
-        verify(mAssistants).setUserSet(10, true);
+        verify(mAssistants).setUserSet(ui.id, true);
         verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
                 c.flattenToString(), user.getIdentifier(), false,  false);
         verify(mListeners, never()).setPackageOrComponentEnabled(
@@ -3116,10 +3116,10 @@
             throws Exception {
         List<UserInfo> uis = new ArrayList<>();
         UserInfo ui = new UserInfo();
-        ui.id = 0;
+        ui.id = mContext.getUserId();
         uis.add(ui);
         UserInfo ui10 = new UserInfo();
-        ui10.id = 10;
+        ui10.id = mContext.getUserId() + 10;
         uis.add(ui10);
         UserHandle user = ui.getUserHandle();
         ArrayList<ComponentName> componentList = new ArrayList<>();
@@ -3135,8 +3135,8 @@
                 c.flattenToString(), user.getIdentifier(), true, false);
         verify(mAssistants, times(1)).setPackageOrComponentEnabled(
                 c.flattenToString(), ui10.id, true, false);
-        verify(mAssistants).setUserSet(0, true);
-        verify(mAssistants).setUserSet(10, true);
+        verify(mAssistants).setUserSet(ui.id, true);
+        verify(mAssistants).setUserSet(ui10.id, true);
         verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
                 c.flattenToString(), user.getIdentifier(), false,  false);
         verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
@@ -3152,7 +3152,7 @@
         mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true);
 
         verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
-                c.getPackageName(), 0, true, true);
+                c.getPackageName(), mContext.getUserId(), true, true);
         verify(mAssistants, never()).setPackageOrComponentEnabled(
                 any(), anyInt(), anyBoolean(), anyBoolean());
         verify(mListeners, never()).setPackageOrComponentEnabled(
@@ -3179,7 +3179,7 @@
         ComponentName c = ComponentName.unflattenFromString("package/Component");
         List<UserInfo> uis = new ArrayList<>();
         UserInfo ui = new UserInfo();
-        ui.id = 0;
+        ui.id = mContext.getUserId();
         uis.add(ui);
         when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
 
@@ -3214,9 +3214,9 @@
         mBinderService.setNotificationListenerAccessGranted(c, true);
 
         verify(mListeners, times(1)).setPackageOrComponentEnabled(
-                c.flattenToString(), 0, true, true);
+                c.flattenToString(), mContext.getUserId(), true, true);
         verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
-                c.flattenToString(), 0, false, true);
+                c.flattenToString(), mContext.getUserId(), false, true);
         verify(mAssistants, never()).setPackageOrComponentEnabled(
                 any(), anyInt(), anyBoolean(), anyBoolean());
     }
@@ -3228,7 +3228,7 @@
         ComponentName c = ComponentName.unflattenFromString("package/Component");
         List<UserInfo> uis = new ArrayList<>();
         UserInfo ui = new UserInfo();
-        ui.id = 0;
+        ui.id = mContext.getUserId();
         uis.add(ui);
         when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
 
@@ -3237,9 +3237,9 @@
         verify(mListeners, never()).setPackageOrComponentEnabled(
                 anyString(), anyInt(), anyBoolean(), anyBoolean());
         verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
-                c.flattenToString(), 0, false, true);
+                c.flattenToString(), ui.id, false, true);
         verify(mAssistants, times(1)).setPackageOrComponentEnabled(
-                c.flattenToString(), 0, true, true);
+                c.flattenToString(), ui.id, true, true);
     }
 
     @Test
@@ -3253,7 +3253,7 @@
         verify(mListeners, never()).setPackageOrComponentEnabled(
                 anyString(), anyInt(), anyBoolean(), anyBoolean());
         verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
-                c.getPackageName(), 0, true, true);
+                c.getPackageName(), mContext.getUserId(), true, true);
         verify(mAssistants, never()).setPackageOrComponentEnabled(
                 any(), anyInt(), anyBoolean(), anyBoolean());
     }
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 477592b..26cea2c 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -837,20 +837,31 @@
                     boolean prevHostConnected = mHostConnected;
                     UsbPort port = (UsbPort) args.arg1;
                     UsbPortStatus status = (UsbPortStatus) args.arg2;
-                    mHostConnected = status.getCurrentDataRole() == DATA_ROLE_HOST;
-                    mSourcePower = status.getCurrentPowerRole() == POWER_ROLE_SOURCE;
-                    mSinkPower = status.getCurrentPowerRole() == POWER_ROLE_SINK;
-                    mAudioAccessoryConnected = (status.getCurrentMode() == MODE_AUDIO_ACCESSORY);
+
+                    if (status != null) {
+                        mHostConnected = status.getCurrentDataRole() == DATA_ROLE_HOST;
+                        mSourcePower = status.getCurrentPowerRole() == POWER_ROLE_SOURCE;
+                        mSinkPower = status.getCurrentPowerRole() == POWER_ROLE_SINK;
+                        mAudioAccessoryConnected = (status.getCurrentMode() == MODE_AUDIO_ACCESSORY);
+
+                        // Ideally we want to see if PR_SWAP and DR_SWAP is supported.
+                        // But, this should be suffice, since, all four combinations are only supported
+                        // when PR_SWAP and DR_SWAP are supported.
+                        mSupportsAllCombinations = status.isRoleCombinationSupported(
+                                POWER_ROLE_SOURCE, DATA_ROLE_HOST)
+                                && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST)
+                                && status.isRoleCombinationSupported(POWER_ROLE_SOURCE,
+                                DATA_ROLE_DEVICE)
+                                && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_DEVICE);
+                    } else {
+                        mHostConnected = false;
+                        mSourcePower = false;
+                        mSinkPower = false;
+                        mAudioAccessoryConnected = false;
+                        mSupportsAllCombinations = false;
+                    }
+
                     mAudioAccessorySupported = port.isModeSupported(MODE_AUDIO_ACCESSORY);
-                    // Ideally we want to see if PR_SWAP and DR_SWAP is supported.
-                    // But, this should be suffice, since, all four combinations are only supported
-                    // when PR_SWAP and DR_SWAP are supported.
-                    mSupportsAllCombinations = status.isRoleCombinationSupported(
-                            POWER_ROLE_SOURCE, DATA_ROLE_HOST)
-                            && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST)
-                            && status.isRoleCombinationSupported(POWER_ROLE_SOURCE,
-                            DATA_ROLE_DEVICE)
-                            && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_DEVICE);
 
                     args.recycle();
                     updateUsbNotification(false);
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index ec7d4bd..647a2a2 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -60,9 +60,9 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.service.ServiceProtoEnums;
 import android.service.usb.UsbPortInfoProto;
 import android.service.usb.UsbPortManagerProto;
-import android.service.usb.UsbServiceProto;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
@@ -992,15 +992,15 @@
     private static int convertContaminantDetectionStatusToProto(int contaminantDetectionStatus) {
         switch (contaminantDetectionStatus) {
             case UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED:
-                return UsbServiceProto.CONTAMINANT_STATUS_NOT_SUPPORTED;
+                return ServiceProtoEnums.CONTAMINANT_STATUS_NOT_SUPPORTED;
             case UsbPortStatus.CONTAMINANT_DETECTION_DISABLED:
-                return UsbServiceProto.CONTAMINANT_STATUS_DISABLED;
+                return ServiceProtoEnums.CONTAMINANT_STATUS_DISABLED;
             case UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED:
-                return UsbServiceProto.CONTAMINANT_STATUS_NOT_DETECTED;
+                return ServiceProtoEnums.CONTAMINANT_STATUS_NOT_DETECTED;
             case UsbPortStatus.CONTAMINANT_DETECTION_DETECTED:
-                return UsbServiceProto.CONTAMINANT_STATUS_DETECTED;
+                return ServiceProtoEnums.CONTAMINANT_STATUS_DETECTED;
             default:
-                return UsbServiceProto.CONTAMINANT_STATUS_UNKNOWN;
+                return ServiceProtoEnums.CONTAMINANT_STATUS_UNKNOWN;
         }
     }
 
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index dc2fb94..f84dd7b 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -338,7 +338,7 @@
      *
      * @param videoState The video state in which to answer the connection.
      */
-    public void onAnswer(int videoState) {}
+    public void onAnswer(@VideoProfile.VideoState int videoState) {}
 
     /**
      * Notifies this Conference, which is in {@code STATE_RINGING}, of
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 580513c..d82205e 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.app.Service;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -1887,6 +1888,7 @@
     /** {@inheritDoc} */
     @Override
     public final IBinder onBind(Intent intent) {
+        onBindClient(intent);
         return mBinder;
     }
 
@@ -1897,6 +1899,13 @@
         return super.onUnbind(intent);
     }
 
+    /**
+     * Used for testing to let the test suite know when the connection service has been bound.
+     * @hide
+     */
+    @TestApi
+    public void onBindClient(@Nullable Intent intent) {
+    }
 
     /**
      * This can be used by telecom to either create a new outgoing conference call or attach
@@ -2544,9 +2553,9 @@
      * @return The {@code Connection} object to satisfy this call, or {@code null} to
      *         not handle the call.
      */
-    public final RemoteConnection createRemoteIncomingConnection(
-            PhoneAccountHandle connectionManagerPhoneAccount,
-            ConnectionRequest request) {
+    public final @Nullable RemoteConnection createRemoteIncomingConnection(
+            @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
+            @NonNull ConnectionRequest request) {
         return mRemoteConnectionManager.createRemoteConnection(
                 connectionManagerPhoneAccount, request, true);
     }
@@ -2563,9 +2572,9 @@
      * @return The {@code Connection} object to satisfy this call, or {@code null} to
      *         not handle the call.
      */
-    public final RemoteConnection createRemoteOutgoingConnection(
-            PhoneAccountHandle connectionManagerPhoneAccount,
-            ConnectionRequest request) {
+    public final @Nullable RemoteConnection createRemoteOutgoingConnection(
+            @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
+            @NonNull ConnectionRequest request) {
         return mRemoteConnectionManager.createRemoteConnection(
                 connectionManagerPhoneAccount, request, false);
     }
@@ -2805,12 +2814,14 @@
      * @param connectionManagerPhoneAccount See description at
      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
      * @param request Details about the incoming conference call.
-     * @return The {@code Conference} object to satisfy this call, or {@code null} to
-     *         not handle the call.
+     * @return The {@code Conference} object to satisfy this call. If the conference attempt is
+     *         failed, the return value will be a result of an invocation of
+     *         {@link Connection#createFailedConnection(DisconnectCause)}.
+     *         Return {@code null} if the {@link ConnectionService} cannot handle the call.
      */
     public @Nullable Conference onCreateIncomingConference(
-            @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
-            @Nullable ConnectionRequest request) {
+            @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
+            @NonNull ConnectionRequest request) {
         return null;
     }
 
@@ -2913,8 +2924,8 @@
      * @param request The outgoing connection request.
      */
     public void onCreateOutgoingConferenceFailed(
-            @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
-            @Nullable ConnectionRequest request) {
+            @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
+            @NonNull ConnectionRequest request) {
     }
 
 
@@ -2978,12 +2989,14 @@
      *         a {@code PhoneAccount} registered by this {@code ConnectionService} to use for
      *         making the connection.
      * @param request Details about the outgoing call.
-     * @return The {@code Conference} object to satisfy this call, or the result of an invocation
-     *         of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call.
+     * @return The {@code Conference} object to satisfy this call. If the conference attempt is
+     *         failed, the return value will be a result of an invocation of
+     *         {@link Connection#createFailedConnection(DisconnectCause)}.
+     *         Return {@code null} if the {@link ConnectionService} cannot handle the call.
      */
     public @Nullable Conference onCreateOutgoingConference(
-            @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
-            @Nullable ConnectionRequest request) {
+            @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
+            @NonNull ConnectionRequest request) {
         return null;
     }
 
diff --git a/telephony/java/android/telephony/CarrierBandwidth.java b/telephony/java/android/telephony/CarrierBandwidth.java
index b153fef..9e1dee0 100644
--- a/telephony/java/android/telephony/CarrierBandwidth.java
+++ b/telephony/java/android/telephony/CarrierBandwidth.java
@@ -101,7 +101,7 @@
     }
 
     /**
-     * Retrieves the upstream bandwidth for the primary network in Kbps.  This always only refers to
+     * Retrieves the upstream bandwidth for the primary network in kbps.  This always only refers to
      * the estimated first hop transport bandwidth.
      * This will be {@link #INVALID} if the network is not connected
      *
@@ -112,7 +112,7 @@
     }
 
     /**
-     * Retrieves the downstream bandwidth for the primary network in Kbps.  This always only refers
+     * Retrieves the downstream bandwidth for the primary network in kbps.  This always only refers
      * to the estimated first hop transport bandwidth.
      * This will be {@link #INVALID} if the network is not connected
      *
@@ -123,7 +123,7 @@
     }
 
     /**
-     * Retrieves the upstream bandwidth for the secondary network in Kbps.  This always only refers
+     * Retrieves the upstream bandwidth for the secondary network in kbps.  This always only refers
      * to the estimated first hop transport bandwidth.
      * <p/>
      * This will be {@link #INVALID} if either are the case:
@@ -143,7 +143,7 @@
     }
 
     /**
-     * Retrieves the downstream bandwidth for the secondary network in Kbps.  This always only
+     * Retrieves the downstream bandwidth for the secondary network in kbps.  This always only
      * refers to the estimated first hop transport bandwidth.
      * <p/>
      * This will be {@link #INVALID} if either are the case:
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 9c9670c..0ba96a9 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -33,13 +33,15 @@
 import android.service.carrier.CarrierService;
 import android.telecom.TelecomManager;
 import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
 import android.telephony.ims.ImsSsData;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.feature.RcsFeature;
 
 import com.android.internal.telephony.ICarrierConfigLoader;
 import com.android.telephony.Rlog;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -106,31 +108,17 @@
      */
     public static final int USSD_OVER_IMS_ONLY       = 3;
 
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = { "CARRIER_NR_AVAILABILITY_" }, value = {
-            CARRIER_NR_AVAILABILITY_NONE,
-            CARRIER_NR_AVAILABILITY_NSA,
-            CARRIER_NR_AVAILABILITY_SA,
-    })
-    public @interface DeviceNrCapability {}
-
-    /**
-     * Indicates CARRIER_NR_AVAILABILITY_NONE determine that the carrier does not enable 5G NR.
-     */
-    public static final int CARRIER_NR_AVAILABILITY_NONE = 0;
-
     /**
      * Indicates CARRIER_NR_AVAILABILITY_NSA determine that the carrier enable the non-standalone
      * (NSA) mode of 5G NR.
      */
-    public static final int CARRIER_NR_AVAILABILITY_NSA = 1 << 0;
+    public static final int CARRIER_NR_AVAILABILITY_NSA = 1;
 
     /**
      * Indicates CARRIER_NR_AVAILABILITY_SA determine that the carrier enable the standalone (SA)
      * mode of 5G NR.
      */
-    public static final int CARRIER_NR_AVAILABILITY_SA = 1 << 1;
+    public static final int CARRIER_NR_AVAILABILITY_SA = 2;
 
     private final Context mContext;
 
@@ -1803,23 +1791,20 @@
             "show_precise_failed_cause_bool";
 
     /**
-     * Bit-field integer to determine whether the carrier enable the non-standalone (NSA) mode of
-     * 5G NR, standalone (SA) mode of 5G NR
+     * A list of carrier nr availability is used to determine whether the carrier enable the
+     * non-standalone (NSA) mode of 5G NR, standalone (SA) mode of 5G NR
      *
-     * <UL>
-     *  <LI>CARRIER_NR_AVAILABILITY_NONE: non-NR = 0 </LI>
-     *  <LI>CARRIER_NR_AVAILABILITY_NSA: NSA = 1 << 0</LI>
-     *  <LI>CARRIER_NR_AVAILABILITY_SA: SA = 1 << 1</LI>
-     * </UL>
-     * <p> The value of this key must be bitwise OR of
-     * {@link #CARRIER_NR_AVAILABILITY_NONE}, {@link #CARRIER_NR_AVAILABILITY_NSA},
-     * {@link #CARRIER_NR_AVAILABILITY_SA}.
+     * <p> The value of list is
+     * {@link #CARRIER_NR_AVAILABILITY_NSA}, or {@link #CARRIER_NR_AVAILABILITY_SA}.
      *
-     * <p> For example, if both NSA and SA are used, the value of key is 3 (1 << 0 | 1 << 1).
-     * If the carrier doesn't support 5G NR, the value of key is 0 (non-NR).
-     * If the key is invalid or not configured, a default value 3 (NSA|SA = 3) will apply.
+     * <p> For example, if both NSA and SA are used, the list value is {
+     * {@link #CARRIER_NR_AVAILABILITY_NSA},{@link #CARRIER_NR_AVAILABILITY_SA}}.
+     * If the carrier doesn't support 5G NR, the value is the empty array.
+     * If the key is invalid or not configured, the default value {
+     * {@link #CARRIER_NR_AVAILABILITY_NSA},{@link #CARRIER_NR_AVAILABILITY_SA}} will apply.
      */
-    public static final String KEY_CARRIER_NR_AVAILABILITY_INT = "carrier_nr_availability_int";
+    public static final String KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY =
+            "carrier_nr_availabilities_int_array";
 
     /**
      * Boolean to decide whether LTE is enabled.
@@ -2894,6 +2879,18 @@
     public static final String KEY_RTT_SUPPORTED_FOR_VT_BOOL = "rtt_supported_for_vt_bool";
 
     /**
+     * Indicates if the carrier supports upgrading a call that was previously an RTT call to VT.
+     */
+    public static final String KEY_VT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_RTT_CALL_BOOL =
+            "vt_upgrade_supported_for_downgraded_rtt_call";
+
+    /**
+     * Indicates if the carrier supports upgrading a call that was previously a VT call to RTT.
+     */
+    public static final String KEY_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL =
+            "rtt_upgrade_supported_for_downgraded_vt_call";
+
+    /**
      * Indicates if the carrier supports upgrading a voice call to an RTT call during the call.
      */
     public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool";
@@ -3941,6 +3938,43 @@
                 KEY_PREFIX + "enable_presence_publish_bool";
 
         /**
+         * Each string in this array contains a mapping between the service-id and version portion
+         * of the service-description element and the associated IMS feature tag(s) that are
+         * associated with each element (see RCC.07 Table 7).
+         * <p>
+         * Each string contains 3 parts, which define the mapping between service-description and
+         * feature tag(s) that must be present in the IMS REGISTER for the RCS service to be
+         * published as part of the RCS PUBLISH procedure:
+         * [service-id]|[version]|[desc]|[feature_tag];[feature_tag];...
+         * <ul>
+         *   <li>[service-id]: the service-id element associated with the RCS capability.</li>
+         *   <li>[version]: The version element associated with that service-id</li>
+         *   <li>[desc]: The optional desecription element associated with that service-id</li>
+         *   <li>[feature_tag];[feature_tag]: The list of all feature tags associated with this
+         *       capability that MUST ALL be present in the IMS registration for this this
+         *       capability to be published to the network.</li>
+         * </ul>
+         * <p>
+         * Features managed by the framework will be considered capable when the ImsService reports
+         * that those services are capable via the
+         * {@link MmTelFeature#notifyCapabilitiesStatusChanged(MmTelFeature.MmTelCapabilities)} or
+         * {@link RcsFeature#notifyCapabilitiesStatusChanged(RcsFeature.RcsImsCapabilities)} APIs.
+         * For RCS services not managed by the framework, the capability of these services are
+         * determined by looking at the feature tags associated with the IMS registration using the
+         * {@link ImsRegistrationAttributes} API and mapping them to the service-description map.
+         * <p>
+         * The framework contains a default value of this key, which is based off of RCC.07
+         * specification. Capabilities based of carrier extensions may be added to this list on a
+         * carrier-by-carrier basis as required in order to support additional services in the
+         * PUBLISH. If this list contains a service-id and version that overlaps with the default,
+         * it will override the framework default.
+         * @hide
+         */
+        @SystemApi
+        public static final String KEY_PUBLISH_SERVICE_DESC_FEATURE_TAG_MAP_OVERRIDE_STRING_ARRAY =
+                KEY_PREFIX + "publish_service_desc_feature_tag_map_override_string_array";
+
+        /**
          * Flag indicating whether or not this carrier supports the exchange of phone numbers with
          * the carrier's RCS presence server in order to retrieve the RCS capabilities of requested
          * contacts used in the RCS User Capability Exchange (UCE) procedure. See RCC.71, section 3
@@ -3992,6 +4026,14 @@
         public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT =
                 KEY_PREFIX + "non_rcs_capabilities_cache_expiration_sec_int";
 
+        /**
+         * Specifies the RCS feature tag allowed for the carrier.
+         *
+         * <p>The values refer to RCC.07 2.4.4.
+         */
+        public static final String KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY =
+                KEY_PREFIX + "rcs_feature_tag_allowed_string_array";
+
         private Ims() {}
 
         private static PersistableBundle getDefaults() {
@@ -3999,10 +4041,33 @@
             defaults.putInt(KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT, 4000);
             defaults.putBoolean(KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false);
             defaults.putBoolean(KEY_ENABLE_PRESENCE_PUBLISH_BOOL, false);
+            defaults.putStringArray(KEY_PUBLISH_SERVICE_DESC_FEATURE_TAG_MAP_OVERRIDE_STRING_ARRAY,
+                    new String[] {});
             defaults.putBoolean(KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL, false);
             defaults.putBoolean(KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, false);
             defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, true);
             defaults.putInt(KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT, 30 * 24 * 60 * 60);
+            defaults.putStringArray(KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY, new String[]{
+                    "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg\"",
+                    "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.largemsg\"",
+                    "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.deferred\"",
+                    "+g.gsma.rcs.cpm.pager-large",
+                    "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session\"",
+                    "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session\"",
+                    "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.fthttp\"",
+                    "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.ftsms\"",
+                    "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.gsma.callcomposer\"",
+                    "+g.gsma.callcomposer",
+                    "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.gsma.callunanswered\"",
+                    "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.gsma.sharedmap\"",
+                    "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.gsma.sharedsketch\"",
+                    "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.geopush\"",
+                    "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.geosms\"",
+                    "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.chatbot\"",
+                    "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.chatbot.sa\"",
+                    "+g.gsma.rcs.botversion=\"#=1,#=2\"",
+                    "+g.gsma.rcs.cpimext"});
+
             return defaults;
         }
     }
@@ -4213,6 +4278,14 @@
     public static final String KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL =
             "store_sim_pin_for_unattended_reboot_bool";
 
+     /**
+     * Determine whether "Enable 2G" toggle can be shown.
+     *
+     * Used to trade privacy/security against potentially reduced carrier coverage for some
+     * carriers.
+     */
+    public static final String KEY_HIDE_ENABLE_2G = "hide_enable_2g_bool";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -4575,6 +4648,8 @@
         sDefaults.putBoolean(KEY_TTY_SUPPORTED_BOOL, true);
         sDefaults.putBoolean(KEY_HIDE_TTY_HCO_VCO_WITH_RTT_BOOL, false);
         sDefaults.putBoolean(KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL, false);
+        sDefaults.putBoolean(KEY_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL, true);
+        sDefaults.putBoolean(KEY_VT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_RTT_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL, true);
         sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null);
@@ -4588,8 +4663,8 @@
         sDefaults.putString(KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING, "");
         sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, true);
         sDefaults.putInt(KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
-        sDefaults.putInt(KEY_CARRIER_NR_AVAILABILITY_INT,
-                CARRIER_NR_AVAILABILITY_NSA | CARRIER_NR_AVAILABILITY_SA);
+        sDefaults.putIntArray(KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
+                new int[]{CARRIER_NR_AVAILABILITY_NSA, CARRIER_NR_AVAILABILITY_SA});
         sDefaults.putBoolean(KEY_LTE_ENABLED_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_TDSCDMA_BOOL, false);
         sDefaults.putStringArray(KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY, null);
@@ -4768,6 +4843,7 @@
         sDefaults.putStringArray(KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY,
                 new String[]{"ia", "default", "ims", "mms", "dun", "emergency"});
         sDefaults.putBoolean(KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL, true);
+        sDefaults.putBoolean(KEY_HIDE_ENABLE_2G, false);
     }
 
     /**
diff --git a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
index 597fe8f..957f683 100644
--- a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
+++ b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
@@ -68,22 +68,22 @@
     public final boolean isEnDcAvailable;
 
     /**
-     * Provides network support info for LTE VoPS and LTE Emergency bearer support
+     * Provides network support info for VoPS and Emergency bearer support
      */
     @Nullable
-    private final LteVopsSupportInfo mLteVopsSupportInfo;
+    private final VopsSupportInfo mVopsSupportInfo;
 
     /**
      * @hide
      */
     DataSpecificRegistrationInfo(
             int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable,
-            boolean isEnDcAvailable, @Nullable LteVopsSupportInfo lteVops) {
+            boolean isEnDcAvailable, @Nullable VopsSupportInfo vops) {
         this.maxDataCalls = maxDataCalls;
         this.isDcNrRestricted = isDcNrRestricted;
         this.isNrAvailable = isNrAvailable;
         this.isEnDcAvailable = isEnDcAvailable;
-        this.mLteVopsSupportInfo = lteVops;
+        this.mVopsSupportInfo = vops;
     }
 
     /**
@@ -97,7 +97,7 @@
         isDcNrRestricted = dsri.isDcNrRestricted;
         isNrAvailable = dsri.isNrAvailable;
         isEnDcAvailable = dsri.isEnDcAvailable;
-        mLteVopsSupportInfo = dsri.mLteVopsSupportInfo;
+        mVopsSupportInfo = dsri.mVopsSupportInfo;
     }
 
     private DataSpecificRegistrationInfo(/* @NonNull */ Parcel source) {
@@ -105,7 +105,7 @@
         isDcNrRestricted = source.readBoolean();
         isNrAvailable = source.readBoolean();
         isEnDcAvailable = source.readBoolean();
-        mLteVopsSupportInfo = LteVopsSupportInfo.CREATOR.createFromParcel(source);
+        mVopsSupportInfo = source.readParcelable(VopsSupportInfo.class.getClassLoader());
     }
 
     @Override
@@ -114,7 +114,7 @@
         dest.writeBoolean(isDcNrRestricted);
         dest.writeBoolean(isNrAvailable);
         dest.writeBoolean(isEnDcAvailable);
-        mLteVopsSupportInfo.writeToParcel(dest, flags);
+        dest.writeParcelable(mVopsSupportInfo, flags);
     }
 
     @Override
@@ -131,15 +131,15 @@
                 .append(" isDcNrRestricted = " + isDcNrRestricted)
                 .append(" isNrAvailable = " + isNrAvailable)
                 .append(" isEnDcAvailable = " + isEnDcAvailable)
-                .append(" " + mLteVopsSupportInfo)
+                .append(" " + mVopsSupportInfo)
                 .append(" }")
                 .toString();
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(maxDataCalls, isDcNrRestricted, isNrAvailable, isEnDcAvailable,
-                mLteVopsSupportInfo);
+        return Objects.hash(maxDataCalls, isDcNrRestricted, isNrAvailable,
+                isEnDcAvailable, mVopsSupportInfo);
     }
 
     @Override
@@ -153,7 +153,7 @@
                 && this.isDcNrRestricted == other.isDcNrRestricted
                 && this.isNrAvailable == other.isNrAvailable
                 && this.isEnDcAvailable == other.isEnDcAvailable
-                && Objects.equals(mLteVopsSupportInfo, other.mLteVopsSupportInfo);
+                && Objects.equals(mVopsSupportInfo, other.mVopsSupportInfo);
     }
 
     public static final @NonNull Parcelable.Creator<DataSpecificRegistrationInfo> CREATOR =
@@ -171,10 +171,26 @@
 
     /**
      * @return The LTE VOPS (Voice over Packet Switched) support information
+     *
+     * @deprecated use {@link #getVopsSupportInfo()}
      */
+    @Deprecated
     @NonNull
     public LteVopsSupportInfo getLteVopsSupportInfo() {
-        return mLteVopsSupportInfo;
+        return mVopsSupportInfo instanceof LteVopsSupportInfo
+                ? (LteVopsSupportInfo) mVopsSupportInfo
+                : new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE,
+                LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE);
     }
 
+    /**
+     * @return The VOPS (Voice over Packet Switched) support information.
+     *
+     * The instance of {@link LTEVopsSupportInfo}, or {@link NrVopsSupportInfo},
+     * null if there is there is no VOPS support information available.
+     */
+    @Nullable
+    public VopsSupportInfo getVopsSupportInfo() {
+        return mVopsSupportInfo;
+    }
 }
diff --git a/telephony/java/android/telephony/DataThrottlingRequest.java b/telephony/java/android/telephony/DataThrottlingRequest.java
index f50bb58..2827e8d 100644
--- a/telephony/java/android/telephony/DataThrottlingRequest.java
+++ b/telephony/java/android/telephony/DataThrottlingRequest.java
@@ -17,6 +17,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.RequiresFeature;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -52,6 +53,9 @@
      * @hide
      */
     @SystemApi
+    @RequiresFeature(
+            enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+            value = TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING)
     public static final int DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER = 1;
 
     /**
@@ -63,6 +67,9 @@
      * @hide
      */
     @SystemApi
+    @RequiresFeature(
+            enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+            value = TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING)
     public static final int DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER = 2;
 
     /**
@@ -76,6 +83,9 @@
      * @hide
      */
     @SystemApi
+    @RequiresFeature(
+            enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+            value = TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING)
     public static final int DATA_THROTTLING_ACTION_HOLD = 3;
 
     /**
diff --git a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl b/telephony/java/android/telephony/LinkCapacityEstimate.aidl
similarity index 76%
copy from packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
copy to telephony/java/android/telephony/LinkCapacityEstimate.aidl
index 7979afc..286f33f 100644
--- a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
+++ b/telephony/java/android/telephony/LinkCapacityEstimate.aidl
@@ -1,12 +1,11 @@
-/**
- *
+/*
  * 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
+ *      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,
@@ -15,9 +14,6 @@
  * limitations under the License.
  */
 
-package android.net;
+package android.telephony;
 
-/** @hide */
-oneway interface IOnSetOemNetworkPreferenceListener {
-    void onComplete();
-}
+parcelable LinkCapacityEstimate;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/LinkCapacityEstimate.java b/telephony/java/android/telephony/LinkCapacityEstimate.java
new file mode 100644
index 0000000..deeb809
--- /dev/null
+++ b/telephony/java/android/telephony/LinkCapacityEstimate.java
@@ -0,0 +1,179 @@
+/*
+ * 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.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Link Capacity Estimate from the modem
+ * @hide
+ */
+@SystemApi
+public final class LinkCapacityEstimate implements Parcelable {
+    /** A value indicates that the capacity estimate is not available */
+    public static final int INVALID = -1;
+
+    /**
+     * LCE for the primary network
+     */
+    public static final int LCE_TYPE_PRIMARY = 0;
+
+    /**
+     * LCE for the secondary network
+     */
+    public static final int LCE_TYPE_SECONDARY = 1;
+
+    /**
+     * Combined LCE for primary network and secondary network reported by the legacy modem
+     */
+    public static final int LCE_TYPE_COMBINED = 2;
+
+    /** @hide */
+    @IntDef(prefix = { "LCE_TYPE_" }, value = {
+            LCE_TYPE_PRIMARY,
+            LCE_TYPE_SECONDARY,
+            LCE_TYPE_COMBINED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface LceType {}
+
+    private final @LceType int mType;
+
+    /** Downlink capacity estimate in kbps */
+    private final int mDownlinkCapacityKbps;
+
+    /** Uplink capacity estimate in kbps */
+    private final int mUplinkCapacityKbps;
+
+    /**
+     * Constructor for link capacity estimate
+     */
+    public LinkCapacityEstimate(@LceType int type,
+            int downlinkCapacityKbps, int uplinkCapacityKbps) {
+        mDownlinkCapacityKbps = downlinkCapacityKbps;
+        mUplinkCapacityKbps = uplinkCapacityKbps;
+        mType = type;
+    }
+
+    /**
+     * @hide
+     */
+    public LinkCapacityEstimate(Parcel in) {
+        mDownlinkCapacityKbps = in.readInt();
+        mUplinkCapacityKbps = in.readInt();
+        mType = in.readInt();
+    }
+
+    /**
+     * Retrieves the type of LCE
+     * @return The type of link capacity estimate
+     */
+    public @LceType int getType() {
+        return mType;
+    }
+
+    /**
+     * Retrieves the downlink bandwidth in Kbps.
+     * This will be {@link #INVALID} if the network is not connected
+     * @return The estimated first hop downstream (network to device) bandwidth.
+     */
+    public int getDownlinkCapacityKbps() {
+        return mDownlinkCapacityKbps;
+    }
+
+    /**
+     * Retrieves the uplink bandwidth in Kbps.
+     * This will be {@link #INVALID} if the network is not connected
+     *
+     * @return The estimated first hop upstream (device to network) bandwidth.
+     */
+    public int getUplinkCapacityKbps() {
+        return mUplinkCapacityKbps;
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder()
+                .append("{mType=")
+                .append(mType)
+                .append(", mDownlinkCapacityKbps=")
+                .append(mDownlinkCapacityKbps)
+                .append(", mUplinkCapacityKbps=")
+                .append(mUplinkCapacityKbps)
+                .append("}")
+                .toString();
+    }
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     * @hide
+     */
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mDownlinkCapacityKbps);
+        dest.writeInt(mUplinkCapacityKbps);
+        dest.writeInt(mType);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (o == null || !(o instanceof LinkCapacityEstimate) || hashCode() != o.hashCode())  {
+            return false;
+        }
+
+        if (this == o) {
+            return true;
+        }
+
+        LinkCapacityEstimate that = (LinkCapacityEstimate) o;
+        return mDownlinkCapacityKbps == that.mDownlinkCapacityKbps
+                && mUplinkCapacityKbps == that.mUplinkCapacityKbps
+                && mType == that.mType;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mDownlinkCapacityKbps, mUplinkCapacityKbps, mType);
+    }
+
+    public static final
+            @android.annotation.NonNull Parcelable.Creator<LinkCapacityEstimate> CREATOR =
+            new Parcelable.Creator() {
+        public LinkCapacityEstimate createFromParcel(Parcel in) {
+            return new LinkCapacityEstimate(in);
+        }
+
+        public LinkCapacityEstimate[] newArray(int size) {
+            return new LinkCapacityEstimate[size];
+        }
+    };
+}
diff --git a/telephony/java/android/telephony/LteVopsSupportInfo.java b/telephony/java/android/telephony/LteVopsSupportInfo.java
index 83e41bf..87761e2 100644
--- a/telephony/java/android/telephony/LteVopsSupportInfo.java
+++ b/telephony/java/android/telephony/LteVopsSupportInfo.java
@@ -21,7 +21,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Parcel;
-import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -32,7 +32,7 @@
  * @hide
  */
 @SystemApi
-public final class LteVopsSupportInfo implements Parcelable {
+public final class LteVopsSupportInfo extends VopsSupportInfo {
 
     /**@hide*/
     @Retention(RetentionPolicy.SOURCE)
@@ -42,7 +42,10 @@
     public @interface LteVopsStatus {}
     /**
      * Indicates information not available from modem.
+     *
+     * @deprecated as no instance will be created in this case
      */
+    @Deprecated
     public static final int LTE_STATUS_NOT_AVAILABLE = 1;
 
     /**
@@ -82,13 +85,38 @@
         return mEmcBearerSupport;
     }
 
+    /**
+     * Returns whether VoPS is supported by the network
+     */
+    @Override
+    public boolean isVopsSupported() {
+        return mVopsSupport == LTE_STATUS_SUPPORTED;
+    }
+
+    /**
+     * Returns whether emergency service is supported by the network
+     */
+    @Override
+    public boolean isEmergencyServiceSupported() {
+        return mEmcBearerSupport == LTE_STATUS_SUPPORTED;
+    }
+
+    /**
+     * Returns whether emergency service fallback is supported by the network
+     */
+    @Override
+    public boolean isEmergencyServiceFallbackSupported() {
+        return false;
+    }
+
     @Override
     public int describeContents() {
         return 0;
     }
 
     @Override
-    public void writeToParcel(Parcel out, int flags) {
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        super.writeToParcel(out, flags, AccessNetworkType.EUTRAN);
         out.writeInt(mVopsSupport);
         out.writeInt(mEmcBearerSupport);
     }
@@ -124,6 +152,8 @@
             new Creator<LteVopsSupportInfo>() {
         @Override
         public LteVopsSupportInfo createFromParcel(Parcel in) {
+            // Skip the type info.
+            in.readInt();
             return new LteVopsSupportInfo(in);
         }
 
@@ -133,6 +163,11 @@
         }
     };
 
+    /** @hide */
+    protected static LteVopsSupportInfo createFromParcelBody(Parcel in) {
+        return new LteVopsSupportInfo(in);
+    }
+
     private LteVopsSupportInfo(Parcel in) {
         mVopsSupport = in.readInt();
         mEmcBearerSupport = in.readInt();
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index a78f813..5fb60d7 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -293,11 +293,12 @@
                                    @Nullable CellIdentity cellIdentity, @Nullable String rplmn,
                                    int maxDataCalls, boolean isDcNrRestricted,
                                    boolean isNrAvailable, boolean isEndcAvailable,
-                                   LteVopsSupportInfo lteVopsSupportInfo) {
+                                   @Nullable VopsSupportInfo vopsSupportInfo) {
         this(domain, transportType, registrationState, accessNetworkTechnology, rejectCause,
                 emergencyOnly, availableServices, cellIdentity, rplmn);
         mDataSpecificInfo = new DataSpecificRegistrationInfo(
-                maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable, lteVopsSupportInfo);
+                maxDataCalls, isDcNrRestricted, isNrAvailable,
+                isEndcAvailable, vopsSupportInfo);
         updateNrState();
     }
 
diff --git a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl b/telephony/java/android/telephony/NrVopsSupportInfo.aidl
similarity index 69%
copy from packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
copy to telephony/java/android/telephony/NrVopsSupportInfo.aidl
index 7979afc..460a589 100644
--- a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
+++ b/telephony/java/android/telephony/NrVopsSupportInfo.aidl
@@ -1,12 +1,11 @@
-/**
- *
- * Copyright (C) 2021 The Android Open Source Project
+/*
+ * Copyright 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
+ *      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,
@@ -15,9 +14,6 @@
  * limitations under the License.
  */
 
-package android.net;
+package android.telephony;
 
-/** @hide */
-oneway interface IOnSetOemNetworkPreferenceListener {
-    void onComplete();
-}
+parcelable NrVopsSupportInfo;
diff --git a/telephony/java/android/telephony/NrVopsSupportInfo.java b/telephony/java/android/telephony/NrVopsSupportInfo.java
new file mode 100644
index 0000000..155ee38
--- /dev/null
+++ b/telephony/java/android/telephony/NrVopsSupportInfo.java
@@ -0,0 +1,260 @@
+/*
+ * 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.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Class stores information related to NR network VoPS support
+ * @hide
+ */
+@SystemApi
+public final class NrVopsSupportInfo extends VopsSupportInfo {
+
+    /**
+     * Indicates network does not support vops
+     */
+    public static final int NR_STATUS_VOPS_NOT_SUPPORTED = 0;
+
+    /**
+     * Indicates network supports vops over 3gpp access.
+     */
+    public static final int NR_STATUS_VOPS_3GPP_SUPPORTED = 1;
+
+    /**
+     * Indicates network supports vops over non 3gpp access
+     */
+    public static final int NR_STATUS_VOPS_NON_3GPP_SUPPORTED = 2;
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+        prefix = {"NR_STATUS_VOPS_"},
+        value = {
+            NR_STATUS_VOPS_NOT_SUPPORTED,
+            NR_STATUS_VOPS_3GPP_SUPPORTED,
+            NR_STATUS_VOPS_NON_3GPP_SUPPORTED
+        })
+    public @interface NrVopsStatus {}
+
+    /**
+     * Indicates network does not support emergency service
+     */
+    public static final int NR_STATUS_EMC_NOT_SUPPORTED = 0;
+
+    /**
+     * Indicates network supports emergency service in NR connected to 5GCN only
+     */
+    public static final int NR_STATUS_EMC_5GCN_ONLY = 1;
+
+    /**
+     * Indicates network supports emergency service in E-UTRA connected to 5GCN only
+     */
+    public static final int NR_STATUS_EMC_EUTRA_5GCN_ONLY = 2;
+
+    /**
+     * Indicates network supports emergency service in NR connected to 5GCN and
+     * E-UTRA connected to 5GCN
+     */
+    public static final int NR_STATUS_EMC_NR_EUTRA_5GCN = 3;
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+        prefix = {"NR_STATUS_EMC_"},
+        value = {
+            NR_STATUS_EMC_NOT_SUPPORTED,
+            NR_STATUS_EMC_5GCN_ONLY,
+            NR_STATUS_EMC_EUTRA_5GCN_ONLY,
+            NR_STATUS_EMC_NR_EUTRA_5GCN
+        })
+    public @interface NrEmcStatus {}
+
+    /**
+     * Indicates network does not support emergency service
+     */
+    public static final int NR_STATUS_EMF_NOT_SUPPORTED = 0;
+
+    /**
+     * Indicates network supports emergency service fallback in NR connected to 5GCN only
+     */
+    public static final int NR_STATUS_EMF_5GCN_ONLY = 1;
+
+    /**
+     * Indicates network supports emergency service fallback in E-UTRA connected to 5GCN only
+     */
+    public static final int NR_STATUS_EMF_EUTRA_5GCN_ONLY = 2;
+
+    /**
+     * Indicates network supports emergency service fallback in NR connected to 5GCN
+     * and E-UTRA connected to 5GCN
+     */
+    public static final int NR_STATUS_EMF_NR_EUTRA_5GCN = 3;
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+        prefix = {"NR_STATUS_EMF_"},
+        value = {
+            NR_STATUS_EMF_NOT_SUPPORTED,
+            NR_STATUS_EMF_5GCN_ONLY,
+            NR_STATUS_EMF_EUTRA_5GCN_ONLY,
+            NR_STATUS_EMF_NR_EUTRA_5GCN
+        })
+    public @interface NrEmfStatus {}
+
+    @NrVopsStatus
+    private final int mVopsSupport;
+    @NrEmcStatus
+    private final int mEmcSupport;
+    @NrEmfStatus
+    private final int mEmfSupport;
+
+    public NrVopsSupportInfo(@NrVopsStatus int vops, @NrEmcStatus int emc, @NrEmcStatus int emf) {
+        mVopsSupport = vops;
+        mEmcSupport = emc;
+        mEmfSupport = emf;
+    }
+
+    /**
+     * Provides the NR VoPS support capability as described in:
+     * 3GPP 24.501 EPS network feature support -> IMS VoPS
+     */
+    public @NrVopsStatus int getVopsSupport() {
+        return mVopsSupport;
+    }
+
+    /**
+     * Provides the NR Emergency bearer support capability as described in:
+     * 3GPP 24.501 EPS network feature support -> EMC, and
+     * 38.331 SIB1 : ims-EmergencySupport
+     */
+    public @NrEmcStatus int getEmcSupport() {
+        return mEmcSupport;
+    }
+
+    /**
+     * Provides the NR emergency service fallback support capability as
+     * described in 3GPP 24.501 EPS network feature support -> EMF
+     */
+    public @NrEmfStatus int getEmfSupport() {
+        return mEmfSupport;
+    }
+
+    /**
+     * Returns whether VoPS is supported by the network
+     */
+    @Override
+    public boolean isVopsSupported() {
+        return mVopsSupport != NR_STATUS_VOPS_NOT_SUPPORTED;
+    }
+
+    /**
+     * Returns whether emergency service is supported by the network
+     */
+    @Override
+    public boolean isEmergencyServiceSupported() {
+        return mEmcSupport != NR_STATUS_EMC_NOT_SUPPORTED;
+    }
+
+    /**
+     * Returns whether emergency service fallback is supported by the network
+     */
+    public boolean isEmergencyServiceFallbackSupported() {
+        return mEmfSupport != NR_STATUS_EMF_NOT_SUPPORTED;
+    }
+
+    /**
+     * Implement the Parcelable interface
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        super.writeToParcel(out, flags, AccessNetworkType.NGRAN);
+        out.writeInt(mVopsSupport);
+        out.writeInt(mEmcSupport);
+        out.writeInt(mEmfSupport);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (o == null || !(o instanceof NrVopsSupportInfo)) {
+            return false;
+        }
+        if (this == o) return true;
+        NrVopsSupportInfo other = (NrVopsSupportInfo) o;
+        return mVopsSupport == other.mVopsSupport
+            && mEmcSupport == other.mEmcSupport
+            && mEmfSupport == other.mEmfSupport;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mVopsSupport, mEmcSupport, mEmfSupport);
+    }
+
+    /**
+     * @return string representation.
+     */
+    @NonNull
+    @Override
+    public String toString() {
+        return ("NrVopsSupportInfo : "
+                + " mVopsSupport = " + mVopsSupport
+                + " mEmcSupport = " + mEmcSupport
+                + " mEmfSupport = " + mEmfSupport);
+    }
+
+    public static final @android.annotation.NonNull Creator<NrVopsSupportInfo> CREATOR =
+            new Creator<NrVopsSupportInfo>() {
+        @Override
+        public NrVopsSupportInfo createFromParcel(Parcel in) {
+            // Skip the type info.
+            in.readInt();
+            return new NrVopsSupportInfo(in);
+        }
+
+        @Override
+        public NrVopsSupportInfo[] newArray(int size) {
+            return new NrVopsSupportInfo[size];
+        }
+    };
+
+    /** @hide */
+    protected static NrVopsSupportInfo createFromParcelBody(Parcel in) {
+        return new NrVopsSupportInfo(in);
+    }
+
+    private NrVopsSupportInfo(Parcel in) {
+        mVopsSupport = in.readInt();
+        mEmcSupport = in.readInt();
+        mEmfSupport = in.readInt();
+    }
+}
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index dfe269c..1c9cd94 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -292,6 +292,14 @@
     }
 
     /**
+     * Return a copy of this PhysicalChannelConfig object but redact all the location info.
+     * @hide
+     */
+    public PhysicalChannelConfig createLocationInfoSanitizedCopy() {
+        return new Builder(this).setPhysicalCellId(PHYSICAL_CELL_ID_UNKNOWN).build();
+    }
+
+    /**
      * @return String representation of the connection status
      * @hide
      */
@@ -540,6 +548,23 @@
             mBand = BAND_UNKNOWN;
         }
 
+        /**
+         * Builder object constructed from existing PhysicalChannelConfig object.
+         * @hide
+         */
+        public Builder(PhysicalChannelConfig config) {
+            mNetworkType = config.getNetworkType();
+            mFrequencyRange = config.getFrequencyRange();
+            mDownlinkChannelNumber = config.getDownlinkChannelNumber();
+            mUplinkChannelNumber = config.getUplinkChannelNumber();
+            mCellBandwidthDownlinkKhz = config.getCellBandwidthDownlinkKhz();
+            mCellBandwidthUplinkKhz = config.getCellBandwidthUplinkKhz();
+            mCellConnectionStatus = config.getConnectionStatus();
+            mContextIds = Arrays.copyOf(config.getContextIds(), config.getContextIds().length);
+            mPhysicalCellId = config.getPhysicalCellId();
+            mBand = config.getBand();
+        }
+
         public PhysicalChannelConfig build() {
             return new PhysicalChannelConfig(this);
         }
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index f110dae..2d06062 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -564,6 +564,7 @@
      * @hide
      */
     @UnsupportedAppUsage
+    @TestApi
     public int getDataRegState() {
         return mDataRegState;
     }
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index b958bff..ce5a8cc 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -2127,7 +2127,7 @@
                 ret = iccISms.getSmsCapacityOnIccForSubscriber(getSubscriptionId());
             }
         } catch (RemoteException ex) {
-            throw new RuntimeException(ex);
+            Log.e(TAG, "getSmsCapacityOnIcc() RemoteException", ex);
         }
         return ret;
     }
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index cfb29f1..5a12865 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -300,9 +300,12 @@
      * @param data Message data.
      * @param isCdma Indicates weather the type of the SMS is CDMA.
      * @return An SmsMessage representing the message.
+     *
+     * @hide
      */
+    @SystemApi
     @Nullable
-    public static SmsMessage createSmsSubmitPdu(@NonNull byte[] data, boolean isCdma) {
+    public static SmsMessage createFromNativeSmsSubmitPdu(@NonNull byte[] data, boolean isCdma) {
         SmsMessageBase wrappedMessage;
 
         if (isCdma) {
@@ -318,23 +321,6 @@
     }
 
     /**
-     * Create an SmsMessage from a native SMS-Submit PDU, specified by Bluetooth Message Access
-     * Profile Specification v1.4.2 5.8.
-     * This is used by Bluetooth MAP profile to decode message when sending non UTF-8 SMS messages.
-     *
-     * @param data Message data.
-     * @param isCdma Indicates weather the type of the SMS is CDMA.
-     * @return An SmsMessage representing the message.
-     *
-     * @hide
-     */
-    @SystemApi
-    @Nullable
-    public static SmsMessage createFromNativeSmsSubmitPdu(@NonNull byte[] data, boolean isCdma) {
-        return null;
-    }
-
-    /**
      * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
      * length in bytes (not hex chars) less the SMSC header
      *
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e3853fa..7c39cf0 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -129,6 +129,8 @@
 import java.util.UUID;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
 /**
  * Provides access to information about the telephony services on
@@ -3116,6 +3118,10 @@
                 return NETWORK_TYPE_BITMASK_LTE_CA;
             case NETWORK_TYPE_NR:
                 return NETWORK_TYPE_BITMASK_NR;
+            case NETWORK_TYPE_IWLAN:
+                return NETWORK_TYPE_BITMASK_IWLAN;
+            case NETWORK_TYPE_IDEN:
+                return (1 << (NETWORK_TYPE_IDEN - 1));
             default:
                 return NETWORK_TYPE_BITMASK_UNKNOWN;
         }
@@ -8224,7 +8230,8 @@
     @IntDef({
             ALLOWED_NETWORK_TYPES_REASON_USER,
             ALLOWED_NETWORK_TYPES_REASON_POWER,
-            ALLOWED_NETWORK_TYPES_REASON_CARRIER
+            ALLOWED_NETWORK_TYPES_REASON_CARRIER,
+            ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AllowedNetworkTypesReason {
@@ -8261,21 +8268,36 @@
     public static final int ALLOWED_NETWORK_TYPES_REASON_CARRIER = 2;
 
     /**
-     * Set the allowed network types of the device and
-     * provide the reason triggering the allowed network change.
+     * To indicate allowed network type change is requested by the user via the 2G toggle.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G = 3;
+
+    /**
+     * Set the allowed network types of the device and provide the reason triggering the allowed
+     * network change.
      * This can be called for following reasons
      * <ol>
      * <li>Allowed network types control by USER {@link #ALLOWED_NETWORK_TYPES_REASON_USER}
      * <li>Allowed network types control by power manager
      * {@link #ALLOWED_NETWORK_TYPES_REASON_POWER}
      * <li>Allowed network types control by carrier {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER}
+     * <li>Allowed network types control by the user-controlled "Allow 2G" toggle
+     * {@link #ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}
      * </ol>
      * This API will result in allowing an intersection of allowed network types for all reasons,
      * including the configuration done through other reasons.
+     *
+     * The functionality of this API with the parameter
+     * {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER} is the same as the API
+     * {@link TelephonyManager#setAllowedNetworkTypes}. Use this API instead of
+     * {@link TelephonyManager#setAllowedNetworkTypes}.
      * <p>
      * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
      * ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then
-     * setAllowedNetworkTypesBitmap is used on the radio interface.  Otherwise,
+     * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise,
      * setPreferredNetworkTypesBitmap is used instead.
      *
      * @param reason the reason the allowed network type change is taking place
@@ -8315,21 +8337,17 @@
      * {@link #getAllowedNetworkTypesForReason} returns allowed network type for a
      * specific reason.
      *
-     * <p>Requires Permission:
-     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
-     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
-     *
      * @param reason the reason the allowed network type change is taking place
      * @return the allowed network type bitmask
      * @throws IllegalStateException    if the Telephony process is not currently available.
      * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed.
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @RequiresFeature(
             enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
             value = TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED)
+    @SystemApi
     public @NetworkTypeBitMask long getAllowedNetworkTypesForReason(
             @AllowedNetworkTypesReason int reason) {
         if (!isValidAllowedNetworkTypesReason(reason)) {
@@ -8358,6 +8376,7 @@
             case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER:
             case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER:
             case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER:
+            case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G:
                 return true;
         }
         return false;
@@ -8373,6 +8392,25 @@
     }
 
     /**
+     * Returns a string representation of the allowed network types{@link NetworkTypeBitMask}.
+     *
+     * @param networkTypeBitmask The bitmask of allowed network types.
+     * @return the name of the allowed network types
+     * @hide
+     */
+    public static String convertNetworkTypeBitmaskToString(
+            @NetworkTypeBitMask long networkTypeBitmask) {
+        String networkTypeName = IntStream.rangeClosed(NETWORK_TYPE_GPRS, NETWORK_TYPE_NR)
+                .filter(x -> {
+                    return (networkTypeBitmask & getBitMaskForNetworkType(x))
+                            == getBitMaskForNetworkType(x);
+                })
+                .mapToObj(x -> getNetworkTypeName(x))
+                .collect(Collectors.joining("|"));
+        return TextUtils.isEmpty(networkTypeName) ? "UNKNOWN" : networkTypeName;
+    }
+
+    /**
      * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
      *
      * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
@@ -11859,12 +11897,6 @@
      * "Data capable" means that this device supports packet-switched
      * data connections over the telephony network.
      * <p>
-     * Note: the meaning of this flag is subtly different from the
-     * PackageManager.FEATURE_TELEPHONY system feature, which is available
-     * on any device with a telephony radio, even if the device is
-     * voice-only.
-     *
-     * @hide
      */
     public boolean isDataCapable() {
         if (mContext == null) return true;
@@ -12079,6 +12111,7 @@
                     NETWORK_TYPE_BITMASK_LTE,
                     NETWORK_TYPE_BITMASK_LTE_CA,
                     NETWORK_TYPE_BITMASK_NR,
+                    NETWORK_TYPE_BITMASK_IWLAN
             })
     public @interface NetworkTypeBitMask {}
 
@@ -13981,33 +14014,6 @@
     }
 
     /**
-     * Get carrier bandwidth. In case of Dual connected network this will report
-     * bandwidth per primary and secondary network.
-     * @return CarrierBandwidth with bandwidth of both primary and secondary carrier.
-     * @throws IllegalStateException if the Telephony process is not currently available.
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    @NonNull
-    public CarrierBandwidth getCarrierBandwidth() {
-        try {
-            ITelephony service = getITelephony();
-            if (service != null) {
-                return service.getCarrierBandwidth(getSubId());
-            } else {
-                throw new IllegalStateException("telephony service is null.");
-            }
-        } catch (RemoteException ex) {
-            Log.e(TAG, "getCarrierBandwidth RemoteException", ex);
-            ex.rethrowFromSystemServer();
-        }
-
-        //Should not reach. Adding return statement to make compiler happy
-        return null;
-    }
-
-    /**
      * Called when userActivity is signalled in the power manager.
      * This should only be called from system Uid.
      * @hide
@@ -14111,6 +14117,10 @@
     /**
      * Enable/Disable E-UTRA-NR Dual Connectivity.
      *
+     * This api is supported only if
+     * {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
+     * ({@link TelephonyManager#CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE})
+     * returns true.
      * @param nrDualConnectivityState expected NR dual connectivity state
      * This can be passed following states
      * <ol>
@@ -14120,12 +14130,14 @@
      * {@link #NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE}
      * </ol>
      * @return operation result.
-     * <p>Requires Permission:
-     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      * @throws IllegalStateException if the Telephony process is not currently available.
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(
+            enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+            value = TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE)
     public @EnableNrDualConnectivityResult int setNrDualConnectivityState(
             @NrDualConnectivityState int nrDualConnectivityState) {
         try {
@@ -14145,15 +14157,21 @@
 
     /**
      * Is E-UTRA-NR Dual Connectivity enabled.
+     * This api is supported only if
+     * {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
+     * ({@link TelephonyManager#CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE})
+     * returns true.
      * @return true if dual connectivity is enabled else false. Enabled state does not mean dual
      * connectivity is active. It means the device is allowed to connect to both primary and
      * secondary cell.
-     * <p>Requires Permission:
-     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
      * @throws IllegalStateException if the Telephony process is not currently available.
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(
+            enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+            value = TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE)
     public boolean isNrDualConnectivityEnabled() {
         try {
             ITelephony telephony = getITelephony();
@@ -14405,11 +14423,34 @@
     public static final String CAPABILITY_ALLOWED_NETWORK_TYPES_USED =
             "CAPABILITY_ALLOWED_NETWORK_TYPES_USED";
 
+    /**
+     * Indicates whether {@link #setNrDualConnectivityState()} and
+     * {@link #isNrDualConnectivityEnabled()} ()} are available.  See comments
+     * on respective methods for more information.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE =
+            "CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE";
+
+    /**
+     * Indicates whether a data throttling request sent with {@link #sendThermalMitigationRequest}
+     * is supported. See comments on {@link #sendThermalMitigationRequest} for more information.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING =
+            "CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING";
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @StringDef(prefix = "CAPABILITY_", value = {
             CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE,
             CAPABILITY_ALLOWED_NETWORK_TYPES_USED,
+            CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE,
+            CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING,
     })
     public @interface RadioInterfaceCapability {}
 
@@ -14519,11 +14560,29 @@
      * and can be used at any time during data throttling to hold onto the current level of data
      * throttling.
      *
+     * <p> If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}({@link
+     * #CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING}) returns false, then sending a {@link
+     * DataThrottlingRequest#DATA_THROTTLING_ACTION_HOLD}, {@link
+     * DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER}, or {@link
+     * DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER} will result in {@link
+     * IllegalArgumentException} being thrown. However, on devices that do not
+     * support data throttling, {@link
+     * DataThrottlingRequest#DATA_THROTTLING_ACTION_NO_DATA_THROTTLING} can still be requested in
+     * order to undo the mitigations above it (i.e {@link
+     * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_VOICE_ONLY} and/or {@link
+     * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_RADIO_OFF}). </p>
+     *
+     * <p> In addition to the {@link Manifest.permission#MODIFY_PHONE_STATE} permission, callers of
+     * this API must also be listed in the device configuration as an authorized app in
+     * {@code packages/services/Telephony/res/values/config.xml} under the
+     * {@code thermal_mitigation_allowlisted_packages} key. </p>
+     *
      * @param thermalMitigationRequest Thermal mitigation request. See {@link
      * ThermalMitigationRequest} for details.
      *
      * @throws IllegalStateException if the Telephony process is not currently available.
-     * @throws IllegalArgumentException if the thermalMitigationRequest had invalid parameters.
+     * @throws IllegalArgumentException if the thermalMitigationRequest had invalid parameters or
+     * if the device's modem does not support data throttling.
      *
      * @hide
      */
@@ -14535,7 +14594,8 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.sendThermalMitigationRequest(getSubId(), thermalMitigationRequest);
+                return telephony.sendThermalMitigationRequest(getSubId(), thermalMitigationRequest,
+                        getOpPackageName());
             }
             throw new IllegalStateException("telephony service is null.");
         } catch (RemoteException ex) {
@@ -14577,6 +14637,11 @@
      */
     public void registerTelephonyCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull TelephonyCallback callback) {
+
+        if (mContext == null) {
+            throw new IllegalStateException("telephony service is null.");
+        }
+
         if (executor == null || callback == null) {
             throw new IllegalArgumentException("TelephonyCallback and executor must be non-null");
         }
@@ -14721,13 +14786,17 @@
      * </ul>
      * @param appType icc application type, like {@link #APPTYPE_USIM} or {@link
      * #APPTYPE_ISIM} or {@link#APPTYPE_UNKNOWN}
-     * @param nafId Network Application Function(NAF) fully qualified domain name and
-     * the selected GBA mode. It shall contain two parts delimited by "@" sign. The first
-     * part is the constant string "3GPP-bootstrapping" (GBA_ME),
-     * "3GPP-bootstrapping-uicc" (GBA_ U), or "3GPP-bootstrapping-digest" (GBA_Digest),
-     * and the latter part shall be the FQDN of the NAF (e.g.
-     * "3GPP-bootstrapping@naf1.operator.com" or "3GPP-bootstrapping-uicc@naf1.operator.com",
-     * or "3GPP-bootstrapping-digest@naf1.operator.com").
+     * @param nafId A URI to specify Network Application Function(NAF) fully qualified domain
+     * name (FQDN) and the selected GBA mode. The authority of the URI must contain two parts
+     * delimited by "@" sign. The first part is the constant string "3GPP-bootstrapping" (GBA_ME),
+     * "3GPP-bootstrapping-uicc" (GBA_ U), or "3GPP-bootstrapping-digest" (GBA_Digest).
+     * The second part shall be the FQDN of the NAF. The scheme of the URI is not actually used
+     * for the authentication, which may be set the same as the resource that the application is
+     * going to access. For example, the nafId can be
+     * "https://3GPP-bootstrapping@naf1.operator.com",
+     * "https://3GPP-bootstrapping-uicc@naf1.operator.com",
+     * "https://3GPP-bootstrapping-digest@naf1.operator.com",
+     * "ftps://3GPP-bootstrapping-digest@naf1.operator.com".
      * @param securityProtocol Security protocol identifier between UE and NAF.  See
      * 3GPP TS 33.220 Annex H. Application can use
      * {@link UaSecurityProtocolIdentifier#createDefaultUaSpId},
@@ -14899,7 +14968,9 @@
     public static final int PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED = 1;
 
     /**
-     * The unattended reboot was not prepared due to generic error.
+     * The unattended reboot was not prepared due to a non-recoverable error. After this error,
+     * the client that manages the unattended reboot should not try to invoke the API again
+     * until the next power cycle.
      * @hide
      */
     @SystemApi
diff --git a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl b/telephony/java/android/telephony/VopsSupportInfo.aidl
similarity index 69%
copy from packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
copy to telephony/java/android/telephony/VopsSupportInfo.aidl
index 7979afc..31c608f 100644
--- a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
+++ b/telephony/java/android/telephony/VopsSupportInfo.aidl
@@ -1,12 +1,11 @@
-/**
- *
- * Copyright (C) 2021 The Android Open Source Project
+/*
+ * Copyright 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
+ *      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,
@@ -15,9 +14,6 @@
  * limitations under the License.
  */
 
-package android.net;
+package android.telephony;
 
-/** @hide */
-oneway interface IOnSetOemNetworkPreferenceListener {
-    void onComplete();
-}
+parcelable VopsSupportInfo;
diff --git a/telephony/java/android/telephony/VopsSupportInfo.java b/telephony/java/android/telephony/VopsSupportInfo.java
new file mode 100644
index 0000000..f89bfa9
--- /dev/null
+++ b/telephony/java/android/telephony/VopsSupportInfo.java
@@ -0,0 +1,103 @@
+/*
+ * 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.telephony;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+
+/**
+ * Abstract base class for the information related to network VoPS support.
+ * This is the base class for XxVopsSupportInfo which represent VoPS support
+ * information for specific network access techonology.
+ * @hide
+ */
+@SuppressLint("ParcelNotFinal")
+@SystemApi
+public abstract class VopsSupportInfo implements Parcelable {
+
+    /**
+     * @hide
+     */
+    public VopsSupportInfo() {}
+
+    /**
+     * Returns whether VoPS is supported by the network
+     */
+    public abstract boolean isVopsSupported();
+
+    /**
+     * Returns whether emergency service is supported by the network
+     */
+    public abstract boolean isEmergencyServiceSupported();
+
+    /**
+     * Returns whether emergency service fallback is supported by the network
+     */
+    public abstract boolean isEmergencyServiceFallbackSupported();
+
+    /**
+     * Implement the Parcelable interface
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public abstract void writeToParcel(@NonNull Parcel dest, int flags);
+
+    /**
+     * Used by child classes for parceling.
+     *
+     * @hide
+     */
+    protected void writeToParcel(@NonNull Parcel dest, int flags, int type) {
+        dest.writeInt(type);
+    }
+
+    /** Implement the Parcelable interface */
+    public static final @android.annotation.NonNull Creator<VopsSupportInfo> CREATOR =
+            new Creator<VopsSupportInfo>() {
+        @Override
+        public VopsSupportInfo createFromParcel(Parcel in) {
+                int type = in.readInt();
+                switch (type) {
+                    case AccessNetworkType.EUTRAN:
+                        return LteVopsSupportInfo.createFromParcelBody(in);
+                    case AccessNetworkType.NGRAN:
+                        return NrVopsSupportInfo.createFromParcelBody(in);
+                    default: throw new RuntimeException("Bad VopsSupportInfo Parcel");
+                }
+        }
+
+        @Override
+        public VopsSupportInfo[] newArray(int size) {
+            return new VopsSupportInfo[size];
+        }
+    };
+
+    @Override
+    public abstract int hashCode();
+
+    @Override
+    public abstract boolean equals(Object o);
+}
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index ca1f861..363e47a 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -254,15 +254,15 @@
     }
 
     /**
-     * The APN is throttled for the duration specified in
-     * {@link DataCallResponse#getRetryDurationMillis}.  Calling this method unthrottles that
-     * APN.
+     * Unthrottles the APN on the current transport.  There is no matching "APN throttle" method.
+     * Instead, the APN is throttled for the time specified in
+     * {@link DataCallResponse#getRetryDurationMillis}.
      * <p/>
      * see: {@link DataCallResponse#getRetryDurationMillis}
      *
      * @param apn Access Point Name defined by the carrier.
      */
-    public void onApnUnthrottled(@NonNull String apn) {
+    public void onApnUnthrottled(final @NonNull String apn) {
         if (mCallback != null) {
             try {
                 if (DBG) Rlog.d(TAG, "onApnUnthrottled");
diff --git a/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
index 041edc0..9bc7a5c 100644
--- a/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
+++ b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
@@ -53,7 +53,7 @@
      *
      * @return the qci of the session
      */
-    public int getQci() {
+    public int getQosIdentifier() {
         return mQci;
     }
 
@@ -66,7 +66,7 @@
      *
      * @return the guaranteed bit rate in kbps
      */
-    public long getGuaranteedUplinkBitRate() {
+    public long getGuaranteedUplinkBitRateKbps() {
         return mGuaranteedUplinkBitRate;
     }
 
@@ -79,7 +79,7 @@
      *
      * @return the guaranteed bit rate in kbps
      */
-    public long getGuaranteedDownlinkBitRate() {
+    public long getGuaranteedDownlinkBitRateKbps() {
         return mGuaranteedDownlinkBitRate;
     }
 
@@ -92,7 +92,7 @@
      *
      * @return the max uplink bit rate in kbps
      */
-    public long getMaxUplinkBitRate() {
+    public long getMaxUplinkBitRateKbps() {
         return mMaxUplinkBitRate;
     }
 
@@ -105,7 +105,7 @@
      *
      * @return the max downlink bit rate in kbps
      */
-    public long getMaxDownlinkBitRate() {
+    public long getMaxDownlinkBitRateKbps() {
         return mMaxDownlinkBitRate;
     }
 
@@ -184,16 +184,6 @@
         mRemoteAddresses = Collections.unmodifiableList(remoteAddresses);
     }
 
-    /**
-     * Creates attributes based off of a parcel
-     * @param in the parcel
-     * @return the attributes
-     */
-    @NonNull
-    public static EpsBearerQosSessionAttributes create(@NonNull final Parcel in) {
-        return new EpsBearerQosSessionAttributes(in);
-    }
-
     @Override
     public int describeContents() {
         return 0;
diff --git a/telephony/java/android/telephony/data/NrQos.java b/telephony/java/android/telephony/data/NrQos.java
index 2011eed..fe124ac 100644
--- a/telephony/java/android/telephony/data/NrQos.java
+++ b/telephony/java/android/telephony/data/NrQos.java
@@ -50,6 +50,18 @@
         return new NrQos(in);
     }
 
+    public int get5Qi() {
+        return fiveQi;
+    }
+
+    public int getQfi() {
+        return qosFlowId;
+    }
+
+    public int getAveragingWindow() {
+        return averagingWindowMs;
+    }
+
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(Qos.QOS_TYPE_NR, dest, flags);
diff --git a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl b/telephony/java/android/telephony/data/NrQosSessionAttributes.aidl
similarity index 76%
copy from packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
copy to telephony/java/android/telephony/data/NrQosSessionAttributes.aidl
index 7979afc..fd3bbb0 100644
--- a/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
+++ b/telephony/java/android/telephony/data/NrQosSessionAttributes.aidl
@@ -1,12 +1,11 @@
-/**
- *
+/*
  * 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
+ *      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,
@@ -15,9 +14,6 @@
  * limitations under the License.
  */
 
-package android.net;
+ package android.telephony.data;
 
-/** @hide */
-oneway interface IOnSetOemNetworkPreferenceListener {
-    void onComplete();
-}
+ parcelable NrQosSessionAttributes;
diff --git a/telephony/java/android/telephony/data/NrQosSessionAttributes.java b/telephony/java/android/telephony/data/NrQosSessionAttributes.java
new file mode 100644
index 0000000..4c37687
--- /dev/null
+++ b/telephony/java/android/telephony/data/NrQosSessionAttributes.java
@@ -0,0 +1,259 @@
+/*
+ * 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.telephony.data;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.net.QosSessionAttributes;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Provides Qos attributes of an NR bearer.
+ *
+ * {@hide}
+ */
+@SystemApi
+public final class NrQosSessionAttributes implements Parcelable, QosSessionAttributes {
+    private static final String TAG = NrQosSessionAttributes.class.getSimpleName();
+    private final int m5Qi;
+    private final @IntRange(from=1, to=63) int mQfi;
+    private final long mMaxUplinkBitRate;
+    private final long mMaxDownlinkBitRate;
+    private final long mGuaranteedUplinkBitRate;
+    private final long mGuaranteedDownlinkBitRate;
+    private final long mAveragingWindow;
+    @NonNull private final List<InetSocketAddress> mRemoteAddresses;
+
+    /**
+     * 5G QOS Identifier (5QI), see 3GPP TS 24.501 and 23.501.
+     * The allowed values are standard values(1-9, 65-68, 69-70, 75, 79-80, 82-85)
+     * defined in the spec and operator specific values in the range 128-254.
+     *
+     * @return the 5QI of the QOS flow
+     */
+    public int getQosIdentifier() {
+        return m5Qi;
+    }
+
+    /**
+     * QOS flow identifier of the QOS flow description in the
+     * range of 1 to 63. see 3GPP TS 24.501 and 23.501.
+     *
+     * @return the QOS flow identifier of the session
+     */
+    public @IntRange(from=1, to=63) int getQosFlowIdentifier() {
+        return mQfi;
+    }
+
+    /**
+     * Minimum bit rate in kbps that is guaranteed to be provided by the network on the uplink.
+     *
+     * see 3GPP TS 24.501 section 6.2.5
+     *
+     * Note: The Qos Session may be shared with OTHER applications besides yours.
+     *
+     * @return the guaranteed bit rate in kbps
+     */
+    public long getGuaranteedUplinkBitRateKbps() {
+        return mGuaranteedUplinkBitRate;
+    }
+
+    /**
+     * Minimum bit rate in kbps that is guaranteed to be provided by the network on the downlink.
+     *
+     * see 3GPP TS 24.501 section 6.2.5
+     *
+     * Note: The Qos Session may be shared with OTHER applications besides yours.
+     *
+     * @return the guaranteed bit rate in kbps
+     */
+    public long getGuaranteedDownlinkBitRateKbps() {
+        return mGuaranteedDownlinkBitRate;
+    }
+
+    /**
+     * The maximum uplink kbps that the network will accept.
+     *
+     * see 3GPP TS 24.501 section 6.2.5
+     *
+     * Note: The Qos Session may be shared with OTHER applications besides yours.
+     *
+     * @return the max uplink bit rate in kbps
+     */
+    public long getMaxUplinkBitRateKbps() {
+        return mMaxUplinkBitRate;
+    }
+
+    /**
+     * The maximum downlink kbps that the network can provide.
+     *
+     * see 3GPP TS 24.501 section 6.2.5
+     *
+     * Note: The Qos Session may be shared with OTHER applications besides yours.
+     *
+     * @return the max downlink bit rate in kbps
+     */
+    public long getMaxDownlinkBitRateKbps() {
+        return mMaxDownlinkBitRate;
+    }
+
+    /**
+     * The duration in milliseconds over which the maximum bit rates and guaranteed bit rates
+     * are calculated
+     *
+     * see 3GPP TS 24.501 section 6.2.5
+     *
+     * @return the averaging window duration in milliseconds
+     */
+    @NonNull
+    public Duration getBitRateWindowDuration() {
+        return Duration.ofMillis(mAveragingWindow);
+    }
+
+    /**
+     * List of remote addresses associated with the Qos Session.  The given uplink bit rates apply
+     * to this given list of remote addresses.
+     *
+     * Note: In the event that the list is empty, it is assumed that the uplink bit rates apply to
+     * all remote addresses that are not contained in a different set of attributes.
+     *
+     * @return list of remote socket addresses that the attributes apply to
+     */
+    @NonNull
+    public List<InetSocketAddress> getRemoteAddresses() {
+        return mRemoteAddresses;
+    }
+
+    /**
+     * ..ctor for attributes
+     *
+     * @param fiveQi 5G quality class indicator
+     * @param qfi QOS flow identifier
+     * @param maxDownlinkBitRate the max downlink bit rate in kbps
+     * @param maxUplinkBitRate the max uplink bit rate in kbps
+     * @param guaranteedDownlinkBitRate the guaranteed downlink bit rate in kbps
+     * @param guaranteedUplinkBitRate the guaranteed uplink bit rate in kbps
+     * @param averagingWindow the averaging window duration in milliseconds
+     * @param remoteAddresses the remote addresses that the uplink bit rates apply to
+     *
+     * @hide
+     */
+    public NrQosSessionAttributes(final int fiveQi, final int qfi,
+            final long maxDownlinkBitRate, final long maxUplinkBitRate,
+            final long guaranteedDownlinkBitRate, final long guaranteedUplinkBitRate,
+            final long averagingWindow, @NonNull final List<InetSocketAddress> remoteAddresses) {
+        Objects.requireNonNull(remoteAddresses, "remoteAddress must be non-null");
+        m5Qi = fiveQi;
+        mQfi = qfi;
+        mMaxDownlinkBitRate = maxDownlinkBitRate;
+        mMaxUplinkBitRate = maxUplinkBitRate;
+        mGuaranteedDownlinkBitRate = guaranteedDownlinkBitRate;
+        mGuaranteedUplinkBitRate = guaranteedUplinkBitRate;
+        mAveragingWindow = averagingWindow;
+
+        final List<InetSocketAddress> remoteAddressesTemp = copySocketAddresses(remoteAddresses);
+        mRemoteAddresses = Collections.unmodifiableList(remoteAddressesTemp);
+    }
+
+    private static List<InetSocketAddress> copySocketAddresses(
+            @NonNull final List<InetSocketAddress> remoteAddresses) {
+        final List<InetSocketAddress> remoteAddressesTemp = new ArrayList<>();
+        for (final InetSocketAddress socketAddress : remoteAddresses) {
+            if (socketAddress != null && socketAddress.getAddress() != null) {
+                remoteAddressesTemp.add(socketAddress);
+            }
+        }
+        return remoteAddressesTemp;
+    }
+
+    private NrQosSessionAttributes(@NonNull final Parcel in) {
+        m5Qi = in.readInt();
+        mQfi = in.readInt();
+        mMaxDownlinkBitRate = in.readLong();
+        mMaxUplinkBitRate = in.readLong();
+        mGuaranteedDownlinkBitRate = in.readLong();
+        mGuaranteedUplinkBitRate = in.readLong();
+        mAveragingWindow = in.readLong();
+
+        final int size = in.readInt();
+        final List<InetSocketAddress> remoteAddresses = new ArrayList<>(size);
+        for (int i = 0; i < size; i++) {
+            final byte[] addressBytes = in.createByteArray();
+            final int port = in.readInt();
+            try {
+                remoteAddresses.add(
+                        new InetSocketAddress(InetAddress.getByAddress(addressBytes), port));
+            } catch (final UnknownHostException e) {
+                // Impossible case since its filtered out the null values in the ..ctor
+                Log.e(TAG, "unable to unparcel remote address at index: " + i, e);
+            }
+        }
+        mRemoteAddresses = Collections.unmodifiableList(remoteAddresses);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull final Parcel dest, final int flags) {
+        dest.writeInt(m5Qi);
+        dest.writeInt(mQfi);
+        dest.writeLong(mMaxDownlinkBitRate);
+        dest.writeLong(mMaxUplinkBitRate);
+        dest.writeLong(mGuaranteedDownlinkBitRate);
+        dest.writeLong(mGuaranteedUplinkBitRate);
+        dest.writeLong(mAveragingWindow);
+
+        final int size = mRemoteAddresses.size();
+        dest.writeInt(size);
+        for (int i = 0; i < size; i++) {
+            final InetSocketAddress address = mRemoteAddresses.get(i);
+            dest.writeByteArray(address.getAddress().getAddress());
+            dest.writeInt(address.getPort());
+        }
+    }
+
+    @NonNull
+    public static final Creator<NrQosSessionAttributes> CREATOR =
+            new Creator<NrQosSessionAttributes>() {
+        @NonNull
+        @Override
+        public NrQosSessionAttributes createFromParcel(@NonNull final Parcel in) {
+            return new NrQosSessionAttributes(in);
+        }
+
+        @NonNull
+        @Override
+        public NrQosSessionAttributes[] newArray(final int size) {
+            return new NrQosSessionAttributes[size];
+        }
+    };
+}
diff --git a/telephony/java/android/telephony/data/SliceInfo.java b/telephony/java/android/telephony/data/SliceInfo.java
index 51857a7..609d111 100644
--- a/telephony/java/android/telephony/data/SliceInfo.java
+++ b/telephony/java/android/telephony/data/SliceInfo.java
@@ -29,7 +29,12 @@
 import java.util.Objects;
 
 /**
- * Represents a S-NSSAI as defined in 3GPP TS 24.501.
+ * Represents a S-NSSAI as defined in 3GPP TS 24.501, which represents a network slice.
+ *
+ * There are 2 main fields that define a slice, SliceServiceType and SliceDifferentiator.
+ * SliceServiceType defines the type of service provided by the slice, and SliceDifferentiator is
+ * used to differentiate between multiple slices of the same type. If the devices is not on HPLMN,
+ * the mappedHplmn versions of these 2 fields indicate the corresponding values in HPLMN.
  *
  * @hide
  */
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index aa9145b..1e80ab7 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -993,6 +993,16 @@
                 }
             }
 
+            @Override
+            public void onPreProvisioningReceived(byte[] configXml) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mLocalCallback.onPreProvisioningReceived(configXml));
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+
             private void setExecutor(Executor executor) {
                 mExecutor = executor;
             }
@@ -1005,7 +1015,7 @@
          * due to various triggers defined in GSMA RCC.14 for ACS(auto configuration
          * server) or other operator defined triggers. If RCS provisioning is already
          * completed at the time of callback registration, then this method shall be
-         * invoked with the current configuration
+         * invoked with the current configuration.
          * @param configXml The RCS configuration XML received by OTA. It is defined
          * by GSMA RCC.07.
          */
@@ -1038,6 +1048,20 @@
          */
         public void onRemoved() {}
 
+        /**
+         * Some carriers using ACS (auto configuration server) may send a carrier-specific
+         * pre-provisioning configuration XML if the user has not been provisioned for RCS
+         * services yet. When this provisioning XML is received, the framework will move
+         * into a "not provisioned" state for RCS. In order for provisioning to proceed,
+         * the application must parse this configuration XML and perform the carrier specific
+         * opt-in flow for RCS services. If the user accepts, {@link #triggerRcsReconfiguration}
+         * must be called in order for the device to move out of this state and try to fetch
+         * the RCS provisioning information.
+         *
+         * @param configXml the pre-provisioning config in carrier specified format.
+         */
+        public void onPreProvisioningReceived(@NonNull byte[] configXml) {}
+
         /**@hide*/
         public final IRcsConfigCallback getBinder() {
             return mBinder;
diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
index 5eb75e7..9c28c36 100644
--- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
+++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
@@ -21,11 +21,16 @@
 import android.annotation.StringDef;
 import android.annotation.SystemApi;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -38,6 +43,17 @@
 @SystemApi
 public final class RcsContactPresenceTuple implements Parcelable {
 
+    private static final String LOG_TAG = "RcsContactPresenceTuple";
+
+    /**
+     * The service ID used to indicate that service discovery via presence is available.
+     * <p>
+     * See RCC.07 v5.0 specification for more information.
+     * @hide
+     */
+    public static final String SERVICE_ID_PRESENCE =
+            "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcse.dp";
+
     /**
      * The service ID used to indicate that MMTEL service is available.
      * <p>
@@ -329,6 +345,13 @@
         public @NonNull @DuplexMode List<String> getUnsupportedDuplexModes() {
             return Collections.unmodifiableList(mUnsupportedDuplexModeList);
         }
+
+        @Override
+        public String toString() {
+            return "servCaps{" + "a=" + mIsAudioCapable + ", v=" + mIsVideoCapable
+                    + ", supported=" + mSupportedDuplexModeList + ", unsupported="
+                    + mUnsupportedDuplexModeList + '}';
+        }
     }
 
     /**
@@ -353,7 +376,8 @@
         }
 
         /**
-         * The optional SIP Contact URI associated with the PIDF tuple element.
+         * The optional SIP Contact URI associated with the PIDF tuple element if the network
+         * expects the user to use the URI instead of the contact URI to contact it.
          */
         public @NonNull Builder setContactUri(@NonNull Uri contactUri) {
             mPresenceTuple.mContactUri = contactUri;
@@ -365,7 +389,7 @@
          * Per RFC3863 section 4.1.7, the timestamp is formatted as an IMPP datetime format
          * string per RFC3339.
          */
-        public @NonNull Builder setTimestamp(@NonNull String timestamp) {
+        public @NonNull Builder setTime(@NonNull Instant timestamp) {
             mPresenceTuple.mTimestamp = timestamp;
             return this;
         }
@@ -397,7 +421,7 @@
     }
 
     private Uri mContactUri;
-    private String mTimestamp;
+    private Instant mTimestamp;
     private @BasicStatus String mStatus;
 
     // The service information in the service-description element.
@@ -416,7 +440,7 @@
 
     private RcsContactPresenceTuple(Parcel in) {
         mContactUri = in.readParcelable(Uri.class.getClassLoader());
-        mTimestamp = in.readString();
+        mTimestamp = convertStringFormatTimeToInstant(in.readString());
         mStatus = in.readString();
         mServiceId = in.readString();
         mServiceVersion = in.readString();
@@ -427,7 +451,7 @@
     @Override
     public void writeToParcel(@NonNull Parcel out, int flags) {
         out.writeParcelable(mContactUri, flags);
-        out.writeString(mTimestamp);
+        out.writeString(convertInstantToStringFormat(mTimestamp));
         out.writeString(mStatus);
         out.writeString(mServiceId);
         out.writeString(mServiceVersion);
@@ -453,6 +477,26 @@
                 }
             };
 
+    // Convert the Instant to the string format
+    private String convertInstantToStringFormat(Instant instant) {
+        if (instant == null) {
+            return "";
+        }
+        return instant.toString();
+    }
+
+    // Convert the time string format to Instant
+    private @Nullable Instant convertStringFormatTimeToInstant(String timestamp) {
+        if (TextUtils.isEmpty(timestamp)) {
+            return null;
+        }
+        try {
+            return DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(timestamp, Instant::from);
+        } catch (DateTimeParseException e) {
+            return null;
+        }
+    }
+
     /** @return the status of the tuple element. */
     public @NonNull @BasicStatus String getStatus() {
         return mStatus;
@@ -474,7 +518,7 @@
     }
 
     /** @return the timestamp element contained in the tuple if it exists */
-    public @Nullable String getTimestamp() {
+    public @Nullable Instant getTime() {
         return mTimestamp;
     }
 
@@ -487,4 +531,36 @@
     public @Nullable ServiceCapabilities getServiceCapabilities() {
         return mServiceCapabilities;
     }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder("{");
+        if (Build.IS_ENG) {
+            builder.append("u=");
+            builder.append(mContactUri);
+        } else {
+            builder.append("u=");
+            builder.append(mContactUri != null ? "XXX" : "null");
+        }
+        builder.append(", id=");
+        builder.append(mServiceId);
+        builder.append(", v=");
+        builder.append(mServiceVersion);
+        builder.append(", s=");
+        builder.append(mStatus);
+        if (mTimestamp != null) {
+            builder.append(", timestamp=");
+            builder.append(mTimestamp);
+        }
+        if (mServiceDescription != null) {
+            builder.append(", servDesc=");
+            builder.append(mServiceDescription);
+        }
+        if (mServiceCapabilities != null) {
+            builder.append(", servCaps=");
+            builder.append(mServiceCapabilities);
+        }
+        builder.append("}");
+        return builder.toString();
+    }
 }
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index fe85502..a133ead 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -28,7 +29,9 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Contains the User Capability Exchange capabilities corresponding to a contact's URI.
@@ -109,7 +112,6 @@
     /**
      * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were
      * queried through SIP OPTIONS.
-     * @hide
      */
     public static final class OptionsBuilder {
 
@@ -150,7 +152,7 @@
          * @param tags the list of the supported feature tags
          * @return this OptionBuilder
          */
-        public @NonNull OptionsBuilder addFeatureTags(@NonNull List<String> tags) {
+        public @NonNull OptionsBuilder addFeatureTags(@NonNull Set<String> tags) {
             mCapabilities.mFeatureTags.addAll(tags);
             return this;
         }
@@ -219,7 +221,7 @@
     private @CapabilityMechanism int mCapabilityMechanism;
     private @RequestResult int mRequestResult;
 
-    private final List<String> mFeatureTags = new ArrayList<>();
+    private final Set<String> mFeatureTags = new HashSet<>();
     private final List<RcsContactPresenceTuple> mPresenceTuples = new ArrayList<>();
 
     private RcsContactUceCapability(@NonNull Uri contactUri, @CapabilityMechanism int mechanism,
@@ -234,7 +236,9 @@
         mCapabilityMechanism = in.readInt();
         mSourceType = in.readInt();
         mRequestResult = in.readInt();
-        in.readStringList(mFeatureTags);
+        List<String> featureTagList = new ArrayList<>();
+        in.readStringList(featureTagList);
+        mFeatureTags.addAll(featureTagList);
         in.readParcelableList(mPresenceTuples, RcsContactPresenceTuple.class.getClassLoader());
     }
 
@@ -244,7 +248,7 @@
         out.writeInt(mCapabilityMechanism);
         out.writeInt(mSourceType);
         out.writeInt(mRequestResult);
-        out.writeStringList(mFeatureTags);
+        out.writeStringList(new ArrayList<>(mFeatureTags));
         out.writeParcelableList(mPresenceTuples, flags);
     }
 
@@ -284,7 +288,20 @@
         if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) {
             return Collections.emptyList();
         }
-        return Collections.unmodifiableList(mFeatureTags);
+        return Collections.unmodifiableList(new ArrayList<>(mFeatureTags));
+    }
+
+    /**
+     * @return The feature tags present in the OPTIONS response from the network.
+     * <p>
+     * Note: this is only populated if {@link #getCapabilityMechanism} is
+     * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_OPTIONS}
+     */
+    public @NonNull Set<String> getFeatureTags() {
+        if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) {
+            return Collections.emptySet();
+        }
+        return Collections.unmodifiableSet(mFeatureTags);
     }
 
     /**
@@ -339,9 +356,45 @@
     }
 
     /**
+     * Retrieve the contact URI requested by the applications.
      * @return the URI representing the contact associated with the capabilities.
      */
     public @NonNull Uri getContactUri() {
         return mContactUri;
     }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder("RcsContactUceCapability");
+        if (mCapabilityMechanism == CAPABILITY_MECHANISM_PRESENCE) {
+            builder.append("(presence) {");
+        } else if (mCapabilityMechanism == CAPABILITY_MECHANISM_OPTIONS) {
+            builder.append("(options) {");
+        } else {
+            builder.append("(?) {");
+        }
+        if (Build.IS_ENG) {
+            builder.append("uri=");
+            builder.append(mContactUri);
+        } else {
+            builder.append("uri (isNull)=");
+            builder.append(mContactUri != null ? "XXX" : "null");
+        }
+        builder.append(", sourceType=");
+        builder.append(mSourceType);
+        builder.append(", requestResult=");
+        builder.append(mRequestResult);
+
+        if (mCapabilityMechanism == CAPABILITY_MECHANISM_PRESENCE) {
+            builder.append(", presenceTuples={");
+            builder.append(mPresenceTuples);
+            builder.append("}");
+        } else if (mCapabilityMechanism == CAPABILITY_MECHANISM_OPTIONS) {
+            builder.append(", featureTags={");
+            builder.append(mFeatureTags);
+            builder.append("}");
+        }
+
+        return builder.toString();
+    }
 }
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index ce8bd7d..dd91026 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -36,6 +36,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -430,7 +432,8 @@
 
         /**
          * The pending request has completed successfully due to all requested contacts information
-         * being delivered.
+         * being delivered. The callback {@link #onCapabilitiesReceived(List)}
+         * for each contacts is required to be called before {@link #onComplete} is called.
          */
         void onComplete();
 
@@ -486,7 +489,7 @@
     @SystemApi
     @RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE,
             Manifest.permission.READ_CONTACTS})
-    public void requestCapabilities(@NonNull List<Uri> contactNumbers,
+    public void requestCapabilities(@NonNull Collection<Uri> contactNumbers,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull CapabilitiesCallback c) throws ImsException {
         if (c == null) {
@@ -538,7 +541,7 @@
 
         try {
             imsRcsController.requestCapabilities(mSubId, mContext.getOpPackageName(),
-                    mContext.getAttributionTag(), contactNumbers, internalCallback);
+                    mContext.getAttributionTag(), new ArrayList(contactNumbers), internalCallback);
         } catch (ServiceSpecificException e) {
             throw new ImsException(e.toString(), e.errorCode);
         } catch (RemoteException e) {
@@ -569,6 +572,10 @@
      * {@link CapabilitiesCallback} is called.
      * @param c A one-time callback for when the request for capabilities completes or there is
      * an error processing the request.
+     * @throws ImsException if the subscription associated with this instance of
+     * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+     * available. This can happen if the ImsService has crashed, for example, or if the subscription
+     * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
      * @hide
      */
     @SystemApi
diff --git a/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
index a217d13..c3d7325 100644
--- a/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
@@ -26,7 +26,8 @@
 import android.telephony.ims.stub.CapabilityExchangeEventListener;
 import android.util.Log;
 
-import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
 
 /**
  * The ICapabilityExchangeEventListener wrapper class to store the listener which is registered by
@@ -84,7 +85,7 @@
      * request to the framework.
      */
     public void onRemoteCapabilityRequest(@NonNull Uri contactUri,
-            @NonNull List<String> remoteCapabilities, @NonNull OptionsRequestCallback callback)
+            @NonNull Set<String> remoteCapabilities, @NonNull OptionsRequestCallback callback)
             throws ImsException {
         ICapabilityExchangeEventListener listener = mListenerBinder;
         if (listener == null) {
@@ -114,7 +115,8 @@
         };
 
         try {
-            listener.onRemoteCapabilityRequest(contactUri, remoteCapabilities, internalCallback);
+            listener.onRemoteCapabilityRequest(contactUri, new ArrayList<>(remoteCapabilities),
+                    internalCallback);
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote capability request exception: " + e);
             throw new ImsException("Remote is not available",
diff --git a/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl b/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl
index 5a8973e..d0853d1 100644
--- a/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl
@@ -25,5 +25,6 @@
     void onAutoConfigurationErrorReceived(int errorCode, String errorString);
     void onConfigurationReset();
     void onRemoved();
+    void onPreProvisioningReceived(in byte[] config);
 }
 
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 85703f8..6315b24 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -47,6 +47,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.HashSet;
 import java.util.List;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.CompletableFuture;
@@ -145,8 +146,8 @@
                 throws RemoteException {
             OptionsResponseCallback callbackWrapper = new RcsOptionsResponseAidlWrapper(callback);
             executeMethodAsync(() -> mReference.getCapabilityExchangeImplBaseInternal()
-                    .sendOptionsCapabilityRequest(contactUri, myCapabilities, callbackWrapper),
-                    "sendOptionsCapabilityRequest");
+                    .sendOptionsCapabilityRequest(contactUri, new HashSet<>(myCapabilities),
+                        callbackWrapper), "sendOptionsCapabilityRequest");
         }
 
         // Call the methods with a clean calling identity on the executor and wait indefinitely for
diff --git a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
index 6295548..a3be8da 100644
--- a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
+++ b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
@@ -26,7 +26,7 @@
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.RcsFeature;
 
-import java.util.List;
+import java.util.Set;
 
 /**
  * The interface that is used by the framework to listen to events from the vendor RCS stack
@@ -98,7 +98,8 @@
      * {@link OptionsRequestCallback#onRespondToCapabilityRequestWithError}.
      * @param contactUri The URI associated with the remote contact that is
      * requesting capabilities.
-     * @param remoteCapabilities The remote contact's capability information.
+     * @param remoteCapabilities The remote contact's capability information. The capability
+     * information is in the format defined in RCC.07 section 2.6.1.3.
      * @param callback The callback of this request which is sent from the remote user.
      * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not
      * currently connected to the framework. This can happen if the {@link RcsFeature} is not
@@ -107,6 +108,6 @@
      * cases when the Telephony stack has crashed.
      */
     void onRemoteCapabilityRequest(@NonNull Uri contactUri,
-            @NonNull List<String> remoteCapabilities,
+            @NonNull Set<String> remoteCapabilities,
             @NonNull OptionsRequestCallback callback) throws ImsException;
 }
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index 34984e0..4dcb7f5 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -542,11 +542,34 @@
         }
         mRcsCallbacks.broadcastAction(c -> {
             try {
-                //TODO compressed by default?
                 c.onAutoConfigurationErrorReceived(errorCode, errorString);
             } catch (RemoteException e) {
                 Log.w(TAG, "dead binder in notifyAutoConfigurationErrorReceived, skipping.");
             }
         });
     }
+
+    /**
+     * Notifies application that pre-provisioning config is received.
+     *
+     * <p>Some carriers using ACS (auto configuration server) may send a carrier-specific
+     * pre-provisioning configuration XML if the user has not been provisioned for RCS
+     * services yet. When such provisioning XML is received, ACS client must call this
+     * method to notify the application with the XML.
+     *
+     * @param configXml the pre-provisioning config in carrier specified format.
+     */
+    public final void notifyPreProvisioningReceived(@NonNull byte[] configXml) {
+        // can be null in testing
+        if (mRcsCallbacks == null) {
+            return;
+        }
+        mRcsCallbacks.broadcastAction(c -> {
+            try {
+                c.onPreProvisioningReceived(configXml);
+            } catch (RemoteException e) {
+                Log.w(TAG, "dead binder in notifyPreProvisioningReceived, skipping.");
+            }
+        });
+    }
 }
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index 908869b..25b9446 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -31,7 +31,9 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.Executor;
 
 /**
@@ -241,7 +243,7 @@
 
         /**
          * Notify the framework of the response to the SUBSCRIBE request from
-         * {@link #subscribeForCapabilities(List, SubscribeResponseCallback)}.
+         * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)}.
          * <p>
          * If the carrier network responds to the SUBSCRIBE request with a 2XX response, then the
          * framework will expect the IMS stack to call {@link #onNotifyCapabilitiesUpdate},
@@ -266,7 +268,7 @@
 
         /**
          * Notify the framework  of the response to the SUBSCRIBE request from
-         * {@link #subscribeForCapabilities(List, SubscribeResponseCallback)} that also
+         * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)} that also
          * includes a reason provided in the “reason” header. See RFC3326 for more
          * information.
          *
@@ -385,13 +387,13 @@
      * {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the
      * framework to finish listening for NOTIFY responses.
      *
-     * @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE
-     * capabilities for.
+     * @param uris A {@link Collection} of the {@link Uri}s that the framework is requesting the
+     * UCE capabilities for.
      * @param cb The callback of the subscribe request.
      */
     // executor used is defined in the constructor.
     @SuppressLint("ExecutorRegistration")
-    public void subscribeForCapabilities(@NonNull List<Uri> uris,
+    public void subscribeForCapabilities(@NonNull Collection<Uri> uris,
             @NonNull SubscribeResponseCallback cb) {
         // Stub - to be implemented by service
         Log.w(LOG_TAG, "subscribeForCapabilities called with no implementation.");
@@ -432,6 +434,7 @@
      * @param contactUri The URI of the remote user that we wish to get the capabilities of.
      * @param myCapabilities The capabilities of this device to send to the remote user.
      * @param callback The callback of this request which is sent from the remote user.
+     * @hide
      */
     // executor used is defined in the constructor.
     @SuppressLint("ExecutorRegistration")
@@ -445,4 +448,27 @@
             // Do not do anything, this is a stub implementation.
         }
     }
+
+    /**
+     * Push one's own capabilities to a remote user via the SIP OPTIONS presence exchange mechanism
+     * in order to receive the capabilities of the remote user in response.
+     * <p>
+     * The implementer must use {@link OptionsResponseCallback} to send the response of
+     * this query from the network back to the framework.
+     * @param contactUri The URI of the remote user that we wish to get the capabilities of.
+     * @param myCapabilities The capabilities of this device to send to the remote user.
+     * @param callback The callback of this request which is sent from the remote user.
+     */
+    // executor used is defined in the constructor.
+    @SuppressLint("ExecutorRegistration")
+    public void sendOptionsCapabilityRequest(@NonNull Uri contactUri,
+            @NonNull Set<String> myCapabilities, @NonNull OptionsResponseCallback callback) {
+        // Stub - to be implemented by service
+        Log.w(LOG_TAG, "sendOptionsCapabilityRequest called with no implementation.");
+        try {
+            callback.onCommandError(COMMAND_CODE_NOT_SUPPORTED);
+        } catch (ImsException e) {
+            // Do not do anything, this is a stub implementation.
+        }
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index f74484b..97078c3 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -31,7 +31,6 @@
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telephony.CallForwardingInfo;
-import android.telephony.CarrierBandwidth;
 import android.telephony.CarrierRestrictionRules;
 import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
@@ -54,6 +53,7 @@
 import android.telephony.VisualVoicemailSmsFilterSettings;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.ims.RcsClientConfiguration;
+import android.telephony.ims.RcsContactUceCapability;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IImsConfigCallback;
@@ -2219,12 +2219,6 @@
     boolean isNrDualConnectivityEnabled(int subId);
 
     /**
-     * Get carrier bandwidth per primary and secondary carrier
-     * @return CarrierBandwidth with bandwidth of both primary and secondary carrier.
-     */
-    CarrierBandwidth getCarrierBandwidth(int subId);
-
-    /**
      * Checks whether the device supports the given capability on the radio interface.
      *
      * @param capability the name of the capability
@@ -2237,10 +2231,12 @@
      *
      * @param subId the id of the subscription
      * @param thermalMitigationRequest holds the parameters necessary for the request.
+     * @param callingPackage the package name of the calling package.
      * @throws InvalidThermalMitigationRequestException if the parametes are invalid.
      */
     int sendThermalMitigationRequest(int subId,
-            in ThermalMitigationRequest thermalMitigationRequest);
+            in ThermalMitigationRequest thermalMitigationRequest,
+            String callingPackage);
 
     /**
      * Get the Generic Bootstrapping Architecture authentication keys
@@ -2331,6 +2327,16 @@
     boolean getCarrierSingleRegistrationEnabled(int subId);
 
     /**
+     * Overrides the ims feature validation result
+     */
+    boolean setImsFeatureValidationOverride(int subId, String enabled);
+
+    /**
+     * Gets the ims feature validation override value
+     */
+    boolean getImsFeatureValidationOverride(int subId);
+
+    /**
      *  Return the mobile provisioning url that is used to launch a browser to allow users to manage
      *  their mobile plan.
      */
@@ -2357,6 +2363,41 @@
      void setDeviceUceEnabled(boolean isEnabled);
 
     /**
+     * Add feature tags to the IMS registration being tracked by UCE and potentially
+     * generate a new PUBLISH to the network.
+     * Note: This is designed for a SHELL command only.
+     */
+    RcsContactUceCapability addUceRegistrationOverrideShell(int subId, in List<String> featureTags);
+
+    /**
+     * Remove feature tags from the IMS registration being tracked by UCE and potentially
+     * generate a new PUBLISH to the network.
+     * Note: This is designed for a SHELL command only.
+     */
+    RcsContactUceCapability removeUceRegistrationOverrideShell(int subId,
+            in List<String> featureTags);
+
+    /**
+     * Clear overridden feature tags in the IMS registration being tracked by UCE and potentially
+     * generate a new PUBLISH to the network.
+     * Note: This is designed for a SHELL command only.
+     */
+    RcsContactUceCapability clearUceRegistrationOverrideShell(int subId);
+
+    /**
+     * Get the latest RcsContactUceCapability structure that is used in SIP PUBLISH procedures.
+     * Note: This is designed for a SHELL command only.
+     */
+    RcsContactUceCapability getLatestRcsContactUceCapabilityShell(int subId);
+
+    /**
+     * Returns the last PIDF XML sent to the network during the last PUBLISH or "none" if the
+     * device does not have an active PUBLISH.
+     * Note: This is designed for a SHELL command only.
+     */
+    String getLastUcePidfXmlShell(int subId);
+
+    /**
      * Set a SignalStrengthUpdateRequest to receive notification when Signal Strength breach the
      * specified thresholds.
      */
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index 8122495..e1a424f 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -70,7 +70,7 @@
         "mockito-target-minus-junit4",
         "net-tests-utils",
         "platform-test-annotations",
-        "service-connectivity",
+        "service-connectivity-pre-jarjar",
         "services.core",
         "services.net",
     ],
@@ -79,6 +79,7 @@
         "android.test.runner",
         "android.test.base",
         "android.test.mock",
+        "ServiceConnectivityResources",
     ],
     jni_libs: [
         "libservice-connectivity",
diff --git a/tests/net/TEST_MAPPING b/tests/net/TEST_MAPPING
index 89fc6ea..d659688 100644
--- a/tests/net/TEST_MAPPING
+++ b/tests/net/TEST_MAPPING
@@ -9,6 +9,23 @@
       "name": "FrameworksNetDeflakeTest"
     }
   ],
+  "auto-postsubmit": [
+    // Test tag for automotive targets. These are only running in postsubmit so as to harden the
+    // automotive targets to avoid introducing additional test flake and build time. The plan for
+    // presubmit testing for auto is to augment the existing tests to cover auto use cases as well.
+    // Additionally, this tag is used in targeted test suites to limit resource usage on the test
+    // infra during the hardening phase.
+    // TODO: this tag to be removed once the above is no longer an issue.
+    {
+      "name": "FrameworksNetTests"
+    },
+    {
+      "name": "FrameworksNetIntegrationTests"
+    },
+    {
+      "name": "FrameworksNetDeflakeTest"
+    }
+  ],
   "imports": [
     {
       "path": "cts/tests/tests/net"
diff --git a/tests/net/common/java/android/net/IpPrefixTest.java b/tests/net/common/java/android/net/IpPrefixTest.java
index 9c0fc7c..50ecb42 100644
--- a/tests/net/common/java/android/net/IpPrefixTest.java
+++ b/tests/net/common/java/android/net/IpPrefixTest.java
@@ -113,6 +113,15 @@
             p = new IpPrefix("f00:::/32");
             fail("Expected IllegalArgumentException: invalid IPv6 address");
         } catch (IllegalArgumentException expected) { }
+
+        p = new IpPrefix("/64");
+        assertEquals("::/64", p.toString());
+
+        p = new IpPrefix("/128");
+        assertEquals("::1/128", p.toString());
+
+        p = new IpPrefix("[2001:db8::123]/64");
+        assertEquals("2001:db8::/64", p.toString());
     }
 
     @Test
diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/tests/net/common/java/android/net/LinkAddressTest.java
index 1eaf30c..2cf3cf9 100644
--- a/tests/net/common/java/android/net/LinkAddressTest.java
+++ b/tests/net/common/java/android/net/LinkAddressTest.java
@@ -53,6 +53,7 @@
 import org.junit.runner.RunWith;
 
 import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.InterfaceAddress;
 import java.net.NetworkInterface;
@@ -117,6 +118,20 @@
         assertEquals(456, address.getScope());
         assertTrue(address.isIpv4());
 
+        address = new LinkAddress("/64", 1 /* flags */, 2 /* scope */);
+        assertEquals(Inet6Address.LOOPBACK, address.getAddress());
+        assertEquals(64, address.getPrefixLength());
+        assertEquals(1, address.getFlags());
+        assertEquals(2, address.getScope());
+        assertTrue(address.isIpv6());
+
+        address = new LinkAddress("[2001:db8::123]/64", 3 /* flags */, 4 /* scope */);
+        assertEquals(InetAddresses.parseNumericAddress("2001:db8::123"), address.getAddress());
+        assertEquals(64, address.getPrefixLength());
+        assertEquals(3, address.getFlags());
+        assertEquals(4, address.getScope());
+        assertTrue(address.isIpv6());
+
         // InterfaceAddress doesn't have a constructor. Fetch some from an interface.
         List<InterfaceAddress> addrs = NetworkInterface.getByName("lo").getInterfaceAddresses();
 
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 5d0e016..1f50e31 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -28,12 +28,16 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
+import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION;
+import static android.net.NetworkCapabilities.REDACT_FOR_LOCAL_MAC_ADDRESS;
+import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
 import static android.net.NetworkCapabilities.RESTRICTED_CAPABILITIES;
 import static android.net.NetworkCapabilities.SIGNAL_STRENGTH_UNSPECIFIED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -44,9 +48,12 @@
 import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES;
 import static android.os.Process.INVALID_UID;
 
+import static com.android.modules.utils.build.SdkLevel.isAtLeastR;
+import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
+import static com.android.testutils.MiscAsserts.assertEmpty;
+import static com.android.testutils.MiscAsserts.assertThrows;
 import static com.android.testutils.ParcelUtils.assertParcelSane;
 import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
-import static com.android.testutils.ParcelUtils.parcelingRoundTrip;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -57,17 +64,17 @@
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
 
-import android.net.wifi.WifiInfo;
 import android.net.wifi.aware.DiscoverySession;
 import android.net.wifi.aware.PeerHandle;
 import android.net.wifi.aware.WifiAwareNetworkSpecifier;
 import android.os.Build;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.ArraySet;
+import android.util.Range;
 
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.modules.utils.build.SdkLevel;
+import com.android.testutils.CompatUtil;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 
@@ -84,6 +91,9 @@
 public class NetworkCapabilitiesTest {
     private static final String TEST_SSID = "TEST_SSID";
     private static final String DIFFERENT_TEST_SSID = "DIFFERENT_TEST_SSID";
+    private static final int TEST_SUBID1 = 1;
+    private static final int TEST_SUBID2 = 2;
+    private static final int TEST_SUBID3 = 3;
 
     @Rule
     public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule();
@@ -91,14 +101,6 @@
     private DiscoverySession mDiscoverySession = Mockito.mock(DiscoverySession.class);
     private PeerHandle mPeerHandle = Mockito.mock(PeerHandle.class);
 
-    private boolean isAtLeastR() {
-        return SdkLevel.isAtLeastR();
-    }
-
-    private boolean isAtLeastS() {
-        return SdkLevel.isAtLeastS();
-    }
-
     @Test
     public void testMaybeMarkCapabilitiesRestricted() {
         // verify EIMS is restricted
@@ -211,7 +213,7 @@
         nc1 = new NetworkCapabilities().addTransportType(TRANSPORT_WIFI);
         nc2 = new NetworkCapabilities()
                 .addTransportType(TRANSPORT_WIFI)
-                .setNetworkSpecifier(new StringNetworkSpecifier("specs"));
+                .setNetworkSpecifier(CompatUtil.makeEthernetNetworkSpecifier("eth42"));
         assertNotEquals("", nc1.describeImmutableDifferences(nc2));
         assertEquals("", nc1.describeImmutableDifferences(nc1));
     }
@@ -240,70 +242,94 @@
     @Test
     public void testSetUids() {
         final NetworkCapabilities netCap = new NetworkCapabilities();
-        final Set<UidRange> uids = new ArraySet<>();
-        uids.add(new UidRange(50, 100));
-        uids.add(new UidRange(3000, 4000));
-        netCap.setUids(uids);
-        assertTrue(netCap.appliesToUid(50));
-        assertTrue(netCap.appliesToUid(80));
-        assertTrue(netCap.appliesToUid(100));
+        // Null uids match all UIDs
+        netCap.setUids(null);
+        assertTrue(netCap.appliesToUid(10));
+        assertTrue(netCap.appliesToUid(200));
         assertTrue(netCap.appliesToUid(3000));
-        assertTrue(netCap.appliesToUid(3001));
-        assertFalse(netCap.appliesToUid(10));
-        assertFalse(netCap.appliesToUid(25));
-        assertFalse(netCap.appliesToUid(49));
-        assertFalse(netCap.appliesToUid(101));
-        assertFalse(netCap.appliesToUid(2000));
-        assertFalse(netCap.appliesToUid(100000));
-
+        assertTrue(netCap.appliesToUid(10010));
         assertTrue(netCap.appliesToUidRange(new UidRange(50, 100)));
         assertTrue(netCap.appliesToUidRange(new UidRange(70, 72)));
         assertTrue(netCap.appliesToUidRange(new UidRange(3500, 3912)));
-        assertFalse(netCap.appliesToUidRange(new UidRange(1, 100)));
-        assertFalse(netCap.appliesToUidRange(new UidRange(49, 100)));
-        assertFalse(netCap.appliesToUidRange(new UidRange(1, 10)));
-        assertFalse(netCap.appliesToUidRange(new UidRange(60, 101)));
-        assertFalse(netCap.appliesToUidRange(new UidRange(60, 3400)));
-
-        NetworkCapabilities netCap2 = new NetworkCapabilities();
-        // A new netcap object has null UIDs, so anything will satisfy it.
-        assertTrue(netCap2.satisfiedByUids(netCap));
-        // Still not equal though.
-        assertFalse(netCap2.equalsUids(netCap));
-        netCap2.setUids(uids);
-        assertTrue(netCap2.satisfiedByUids(netCap));
-        assertTrue(netCap.equalsUids(netCap2));
-        assertTrue(netCap2.equalsUids(netCap));
-
-        uids.add(new UidRange(600, 700));
-        netCap2.setUids(uids);
-        assertFalse(netCap2.satisfiedByUids(netCap));
-        assertFalse(netCap.appliesToUid(650));
-        assertTrue(netCap2.appliesToUid(650));
-        netCap.combineCapabilities(netCap2);
-        assertTrue(netCap2.satisfiedByUids(netCap));
-        assertTrue(netCap.appliesToUid(650));
-        assertFalse(netCap.appliesToUid(500));
-
-        assertTrue(new NetworkCapabilities().satisfiedByUids(netCap));
-        netCap.combineCapabilities(new NetworkCapabilities());
-        assertTrue(netCap.appliesToUid(500));
         assertTrue(netCap.appliesToUidRange(new UidRange(1, 100000)));
-        assertFalse(netCap2.appliesToUid(500));
-        assertFalse(netCap2.appliesToUidRange(new UidRange(1, 100000)));
-        assertTrue(new NetworkCapabilities().satisfiedByUids(netCap));
+
+        if (isAtLeastS()) {
+            final Set<Range<Integer>> uids = new ArraySet<>();
+            uids.add(uidRange(50, 100));
+            uids.add(uidRange(3000, 4000));
+            netCap.setUids(uids);
+            assertTrue(netCap.appliesToUid(50));
+            assertTrue(netCap.appliesToUid(80));
+            assertTrue(netCap.appliesToUid(100));
+            assertTrue(netCap.appliesToUid(3000));
+            assertTrue(netCap.appliesToUid(3001));
+            assertFalse(netCap.appliesToUid(10));
+            assertFalse(netCap.appliesToUid(25));
+            assertFalse(netCap.appliesToUid(49));
+            assertFalse(netCap.appliesToUid(101));
+            assertFalse(netCap.appliesToUid(2000));
+            assertFalse(netCap.appliesToUid(100000));
+
+            assertTrue(netCap.appliesToUidRange(new UidRange(50, 100)));
+            assertTrue(netCap.appliesToUidRange(new UidRange(70, 72)));
+            assertTrue(netCap.appliesToUidRange(new UidRange(3500, 3912)));
+            assertFalse(netCap.appliesToUidRange(new UidRange(1, 100)));
+            assertFalse(netCap.appliesToUidRange(new UidRange(49, 100)));
+            assertFalse(netCap.appliesToUidRange(new UidRange(1, 10)));
+            assertFalse(netCap.appliesToUidRange(new UidRange(60, 101)));
+            assertFalse(netCap.appliesToUidRange(new UidRange(60, 3400)));
+
+            NetworkCapabilities netCap2 = new NetworkCapabilities();
+            // A new netcap object has null UIDs, so anything will satisfy it.
+            assertTrue(netCap2.satisfiedByUids(netCap));
+            // Still not equal though.
+            assertFalse(netCap2.equalsUids(netCap));
+            netCap2.setUids(uids);
+            assertTrue(netCap2.satisfiedByUids(netCap));
+            assertTrue(netCap.equalsUids(netCap2));
+            assertTrue(netCap2.equalsUids(netCap));
+
+            uids.add(uidRange(600, 700));
+            netCap2.setUids(uids);
+            assertFalse(netCap2.satisfiedByUids(netCap));
+            assertFalse(netCap.appliesToUid(650));
+            assertTrue(netCap2.appliesToUid(650));
+            netCap.combineCapabilities(netCap2);
+            assertTrue(netCap2.satisfiedByUids(netCap));
+            assertTrue(netCap.appliesToUid(650));
+            assertFalse(netCap.appliesToUid(500));
+
+            assertTrue(new NetworkCapabilities().satisfiedByUids(netCap));
+            netCap.combineCapabilities(new NetworkCapabilities());
+            assertTrue(netCap.appliesToUid(500));
+            assertTrue(netCap.appliesToUidRange(new UidRange(1, 100000)));
+            assertFalse(netCap2.appliesToUid(500));
+            assertFalse(netCap2.appliesToUidRange(new UidRange(1, 100000)));
+            assertTrue(new NetworkCapabilities().satisfiedByUids(netCap));
+
+            // Null uids satisfies everything.
+            netCap.setUids(null);
+            assertTrue(netCap2.satisfiedByUids(netCap));
+            assertTrue(netCap.satisfiedByUids(netCap2));
+            netCap2.setUids(null);
+            assertTrue(netCap2.satisfiedByUids(netCap));
+            assertTrue(netCap.satisfiedByUids(netCap2));
+        }
     }
 
     @Test
     public void testParcelNetworkCapabilities() {
-        final Set<UidRange> uids = new ArraySet<>();
-        uids.add(new UidRange(50, 100));
-        uids.add(new UidRange(3000, 4000));
+        final Set<Range<Integer>> uids = new ArraySet<>();
+        uids.add(uidRange(50, 100));
+        uids.add(uidRange(3000, 4000));
         final NetworkCapabilities netCap = new NetworkCapabilities()
             .addCapability(NET_CAPABILITY_INTERNET)
-            .setUids(uids)
             .addCapability(NET_CAPABILITY_EIMS)
             .addCapability(NET_CAPABILITY_NOT_METERED);
+        if (isAtLeastS()) {
+            netCap.setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2));
+            netCap.setUids(uids);
+        }
         if (isAtLeastR()) {
             netCap.setOwnerUid(123);
             netCap.setAdministratorUids(new int[] {5, 11});
@@ -328,58 +354,9 @@
         testParcelSane(netCap);
     }
 
-    private NetworkCapabilities createNetworkCapabilitiesWithWifiInfo() {
-        // uses a real WifiInfo to test parceling of sensitive data.
-        final WifiInfo wifiInfo = new WifiInfo.Builder()
-                .setSsid("sssid1234".getBytes())
-                .setBssid("00:11:22:33:44:55")
-                .build();
-        return new NetworkCapabilities()
-                .addCapability(NET_CAPABILITY_INTERNET)
-                .addCapability(NET_CAPABILITY_EIMS)
-                .addCapability(NET_CAPABILITY_NOT_METERED)
-                .setSSID(TEST_SSID)
-                .setTransportInfo(wifiInfo)
-                .setRequestorPackageName("com.android.test")
-                .setRequestorUid(9304);
-    }
-
-    @Test
-    public void testParcelNetworkCapabilitiesWithLocationSensitiveFields() {
-        assumeTrue(isAtLeastS());
-
-        final NetworkCapabilities netCap = createNetworkCapabilitiesWithWifiInfo();
-        final NetworkCapabilities netCapWithLocationSensitiveFields =
-                new NetworkCapabilities(netCap, true);
-
-        assertParcelingIsLossless(netCapWithLocationSensitiveFields);
-        testParcelSane(netCapWithLocationSensitiveFields);
-
-        assertEquals(netCapWithLocationSensitiveFields,
-                parcelingRoundTrip(netCapWithLocationSensitiveFields));
-    }
-
-    @Test
-    public void testParcelNetworkCapabilitiesWithoutLocationSensitiveFields() {
-        assumeTrue(isAtLeastS());
-
-        final NetworkCapabilities netCap = createNetworkCapabilitiesWithWifiInfo();
-        final NetworkCapabilities netCapWithoutLocationSensitiveFields =
-                new NetworkCapabilities(netCap, false);
-
-        final NetworkCapabilities sanitizedNetCap =
-                new NetworkCapabilities(netCapWithoutLocationSensitiveFields);
-        final WifiInfo sanitizedWifiInfo = new WifiInfo.Builder()
-                .setSsid(new byte[0])
-                .setBssid(WifiInfo.DEFAULT_MAC_ADDRESS)
-                .build();
-        sanitizedNetCap.setTransportInfo(sanitizedWifiInfo);
-        assertEquals(sanitizedNetCap, parcelingRoundTrip(netCapWithoutLocationSensitiveFields));
-    }
-
     private void testParcelSane(NetworkCapabilities cap) {
         if (isAtLeastS()) {
-            assertParcelSane(cap, 16);
+            assertParcelSane(cap, 17);
         } else if (isAtLeastR()) {
             assertParcelSane(cap, 15);
         } else {
@@ -387,6 +364,45 @@
         }
     }
 
+    private static NetworkCapabilities createNetworkCapabilitiesWithTransportInfo() {
+        return new NetworkCapabilities()
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .addCapability(NET_CAPABILITY_EIMS)
+                .addCapability(NET_CAPABILITY_NOT_METERED)
+                .setSSID(TEST_SSID)
+                .setTransportInfo(new TestTransportInfo())
+                .setRequestorPackageName("com.android.test")
+                .setRequestorUid(9304);
+    }
+
+    @Test
+    public void testNetworkCapabilitiesCopyWithNoRedactions() {
+        assumeTrue(isAtLeastS());
+
+        final NetworkCapabilities netCap = createNetworkCapabilitiesWithTransportInfo();
+        final NetworkCapabilities netCapWithNoRedactions =
+                new NetworkCapabilities(netCap, NetworkCapabilities.REDACT_NONE);
+        TestTransportInfo testTransportInfo =
+                (TestTransportInfo) netCapWithNoRedactions.getTransportInfo();
+        assertFalse(testTransportInfo.locationRedacted);
+        assertFalse(testTransportInfo.localMacAddressRedacted);
+        assertFalse(testTransportInfo.settingsRedacted);
+    }
+
+    @Test
+    public void testNetworkCapabilitiesCopyWithoutLocationSensitiveFields() {
+        assumeTrue(isAtLeastS());
+
+        final NetworkCapabilities netCap = createNetworkCapabilitiesWithTransportInfo();
+        final NetworkCapabilities netCapWithNoRedactions =
+                new NetworkCapabilities(netCap, REDACT_FOR_ACCESS_FINE_LOCATION);
+        TestTransportInfo testTransportInfo =
+                (TestTransportInfo) netCapWithNoRedactions.getTransportInfo();
+        assertTrue(testTransportInfo.locationRedacted);
+        assertFalse(testTransportInfo.localMacAddressRedacted);
+        assertFalse(testTransportInfo.settingsRedacted);
+    }
+
     @Test
     public void testOemPaid() {
         NetworkCapabilities nc = new NetworkCapabilities();
@@ -516,11 +532,22 @@
         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.removeCapability(NET_CAPABILITY_INTERNET);
+            assertTrue(nc1.equalsNetCapabilities(nc2));
 
-        nc1.removeCapability(NET_CAPABILITY_INTERNET);
-        assertFalse(nc1.equalsNetCapabilities(nc2));
-        nc2.removeCapability(NET_CAPABILITY_INTERNET);
-        assertTrue(nc1.equalsNetCapabilities(nc2));
+            nc1.removeUnwantedCapability(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);
+            assertTrue(nc1.equalsNetCapabilities(nc2));
+        }
     }
 
     @Test
@@ -538,12 +565,16 @@
         assertFalse(nc1.satisfiedByNetworkCapabilities(nc2));
     }
 
-    private ArraySet<UidRange> uidRange(int from, int to) {
-        final ArraySet<UidRange> range = new ArraySet<>(1);
-        range.add(new UidRange(from, to));
+    private ArraySet<Range<Integer>> uidRanges(int from, int to) {
+        final ArraySet<Range<Integer>> range = new ArraySet<>(1);
+        range.add(uidRange(from, to));
         return range;
     }
 
+    private Range<Integer> uidRange(int from, int to) {
+        return new Range<Integer>(from, to);
+    }
+
     @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testSetAdministratorUids() {
         NetworkCapabilities nc =
@@ -577,11 +608,21 @@
         // This will effectively move NOT_ROAMING capability from required to unwanted for nc1.
         nc1.addUnwantedCapability(NET_CAPABILITY_NOT_ROAMING);
 
-        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));
+        if (isAtLeastS()) {
+            // From S, it is not allowed to have the same capability in both wanted and
+            // unwanted 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);
+        }
 
         nc1.setSSID(TEST_SSID);
         nc2.combineCapabilities(nc1);
@@ -599,20 +640,34 @@
         } catch (IllegalStateException expected) {}
         nc1.setSSID(TEST_SSID);
 
-        nc1.setUids(uidRange(10, 13));
-        assertNotEquals(nc1, nc2);
-        nc2.combineCapabilities(nc1);  // Everything + 10~13 is still everything.
-        assertNotEquals(nc1, nc2);
-        nc1.combineCapabilities(nc2);  // 10~13 + everything is everything.
-        assertEquals(nc1, nc2);
-        nc1.setUids(uidRange(10, 13));
-        nc2.setUids(uidRange(20, 23));
-        assertNotEquals(nc1, nc2);
-        nc1.combineCapabilities(nc2);
-        assertTrue(nc1.appliesToUid(12));
-        assertFalse(nc2.appliesToUid(12));
-        assertTrue(nc1.appliesToUid(22));
-        assertTrue(nc2.appliesToUid(22));
+        if (isAtLeastS()) {
+            nc1.setUids(uidRanges(10, 13));
+            assertNotEquals(nc1, nc2);
+            nc2.combineCapabilities(nc1);  // Everything + 10~13 is still everything.
+            assertNotEquals(nc1, nc2);
+            nc1.combineCapabilities(nc2);  // 10~13 + everything is everything.
+            assertEquals(nc1, nc2);
+            nc1.setUids(uidRanges(10, 13));
+            nc2.setUids(uidRanges(20, 23));
+            assertNotEquals(nc1, nc2);
+            nc1.combineCapabilities(nc2);
+            assertTrue(nc1.appliesToUid(12));
+            assertFalse(nc2.appliesToUid(12));
+            assertTrue(nc1.appliesToUid(22));
+            assertTrue(nc2.appliesToUid(22));
+
+            // Verify the subscription id list can be combined only when they are equal.
+            nc1.setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2));
+            nc2.setSubIds(Set.of(TEST_SUBID2));
+            assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1));
+
+            nc2.setSubIds(Set.of());
+            assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1));
+
+            nc2.setSubIds(Set.of(TEST_SUBID2, TEST_SUBID1));
+            nc2.combineCapabilities(nc1);
+            assertEquals(Set.of(TEST_SUBID2, TEST_SUBID1), nc2.getSubIds());
+        }
     }
 
     @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
@@ -671,7 +726,7 @@
         NetworkCapabilities nc1 = new NetworkCapabilities();
         nc1.addTransportType(TRANSPORT_CELLULAR).addTransportType(TRANSPORT_WIFI);
         try {
-            nc1.setNetworkSpecifier(new StringNetworkSpecifier("specs"));
+            nc1.setNetworkSpecifier(CompatUtil.makeEthernetNetworkSpecifier("eth0"));
             fail("Cannot set NetworkSpecifier on a NetworkCapability with multiple transports!");
         } catch (IllegalStateException expected) {
             // empty
@@ -680,7 +735,7 @@
         // Sequence 2: Transport + NetworkSpecifier + Transport
         NetworkCapabilities nc2 = new NetworkCapabilities();
         nc2.addTransportType(TRANSPORT_CELLULAR).setNetworkSpecifier(
-                new StringNetworkSpecifier("specs"));
+                CompatUtil.makeEthernetNetworkSpecifier("testtap3"));
         try {
             nc2.addTransportType(TRANSPORT_WIFI);
             fail("Cannot set a second TransportType of a network which has a NetworkSpecifier!");
@@ -757,10 +812,31 @@
         if (isAtLeastR()) {
             assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSsid()));
         }
-
-        nc1.setUids(uidRange(10, 13));
+        if (isAtLeastS()) {
+            nc1.setUids(uidRanges(10, 13));
+        } else {
+            nc1.setUids(null);
+        }
         nc2.set(nc1);  // Overwrites, as opposed to combineCapabilities
         assertEquals(nc1, nc2);
+
+        if (isAtLeastS()) {
+            assertThrows(NullPointerException.class, () -> nc1.setSubIds(null));
+            nc1.setSubIds(Set.of());
+            nc2.set(nc1);
+            assertEquals(nc1, nc2);
+
+            nc1.setSubIds(Set.of(TEST_SUBID1));
+            nc2.set(nc1);
+            assertEquals(nc1, nc2);
+
+            nc2.setSubIds(Set.of(TEST_SUBID2, TEST_SUBID1));
+            nc2.set(nc1);
+            assertEquals(nc1, nc2);
+
+            nc2.setSubIds(Set.of(TEST_SUBID3, TEST_SUBID2));
+            assertNotEquals(nc1, nc2);
+        }
     }
 
     @Test
@@ -841,6 +917,50 @@
         } catch (NullPointerException expected) { }
     }
 
+    private static NetworkCapabilities capsWithSubIds(Integer ... subIds) {
+        // Since the NetworkRequest would put NOT_VCN_MANAGED capabilities in general, for
+        // every NetworkCapabilities that simulates networks needs to add it too in order to
+        // satisfy these requests.
+        final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+                .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+                .setSubIds(new ArraySet<>(subIds)).build();
+        assertEquals(new ArraySet<>(subIds), nc.getSubIds());
+        return nc;
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+    public void testSubIds() throws Exception {
+        final NetworkCapabilities ncWithoutId = capsWithSubIds();
+        final NetworkCapabilities ncWithId = capsWithSubIds(TEST_SUBID1);
+        final NetworkCapabilities ncWithOtherIds = capsWithSubIds(TEST_SUBID1, TEST_SUBID3);
+        final NetworkCapabilities ncWithoutRequestedIds = capsWithSubIds(TEST_SUBID3);
+
+        final NetworkRequest requestWithoutId = new NetworkRequest.Builder().build();
+        assertEmpty(requestWithoutId.networkCapabilities.getSubIds());
+        final NetworkRequest requestWithIds = new NetworkRequest.Builder()
+                .setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2)).build();
+        assertEquals(Set.of(TEST_SUBID1, TEST_SUBID2),
+                requestWithIds.networkCapabilities.getSubIds());
+
+        assertFalse(requestWithIds.canBeSatisfiedBy(ncWithoutId));
+        assertTrue(requestWithIds.canBeSatisfiedBy(ncWithOtherIds));
+        assertFalse(requestWithIds.canBeSatisfiedBy(ncWithoutRequestedIds));
+        assertTrue(requestWithIds.canBeSatisfiedBy(ncWithId));
+        assertTrue(requestWithoutId.canBeSatisfiedBy(ncWithoutId));
+        assertTrue(requestWithoutId.canBeSatisfiedBy(ncWithId));
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+    public void testEqualsSubIds() throws Exception {
+        assertEquals(capsWithSubIds(), capsWithSubIds());
+        assertNotEquals(capsWithSubIds(), capsWithSubIds(TEST_SUBID1));
+        assertEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID1));
+        assertNotEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID2));
+        assertNotEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID2, TEST_SUBID1));
+        assertEquals(capsWithSubIds(TEST_SUBID1, TEST_SUBID2),
+                capsWithSubIds(TEST_SUBID2, TEST_SUBID1));
+    }
+
     @Test
     public void testLinkBandwidthKbps() {
         final NetworkCapabilities nc = new NetworkCapabilities();
@@ -955,18 +1075,42 @@
         } catch (IllegalArgumentException e) { }
     }
 
-    private class TestTransportInfo implements TransportInfo {
+    /**
+     * Test TransportInfo to verify redaction mechanism.
+     */
+    private static class TestTransportInfo implements TransportInfo {
+        public final boolean locationRedacted;
+        public final boolean localMacAddressRedacted;
+        public final boolean settingsRedacted;
+
         TestTransportInfo() {
+            locationRedacted = false;
+            localMacAddressRedacted = false;
+            settingsRedacted = false;
+        }
+
+        TestTransportInfo(boolean locationRedacted,
+                boolean localMacAddressRedacted,
+                boolean settingsRedacted) {
+            this.locationRedacted = locationRedacted;
+            this.localMacAddressRedacted =
+                    localMacAddressRedacted;
+            this.settingsRedacted = settingsRedacted;
         }
 
         @Override
-        public TransportInfo makeCopy(boolean parcelLocationSensitiveFields) {
-            return this;
+        public TransportInfo makeCopy(@NetworkCapabilities.RedactionType long redactions) {
+            return new TestTransportInfo(
+                    (redactions & NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION) != 0,
+                    (redactions & REDACT_FOR_LOCAL_MAC_ADDRESS) != 0,
+                    (redactions & REDACT_FOR_NETWORK_SETTINGS) != 0
+            );
         }
 
         @Override
-        public boolean hasLocationSensitiveFields() {
-            return false;
+        public @NetworkCapabilities.RedactionType long getApplicableRedactions() {
+            return REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS
+                    | REDACT_FOR_NETWORK_SETTINGS;
         }
     }
 
@@ -977,7 +1121,7 @@
         final int requestUid = 10100;
         final int[] administratorUids = {ownerUid, 10001};
         final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier(1);
-        final TestTransportInfo transportInfo = new TestTransportInfo();
+        final TransportInfo transportInfo = new TransportInfo() {};
         final String ssid = "TEST_SSID";
         final String packageName = "com.google.test.networkcapabilities";
         final NetworkCapabilities nc = new NetworkCapabilities.Builder()
@@ -1021,5 +1165,11 @@
             fail("Should not set null into NetworkCapabilities.Builder");
         } catch (NullPointerException expected) { }
         assertEquals(nc, new NetworkCapabilities.Builder(nc).build());
+
+        if (isAtLeastS()) {
+            final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
+                    .setSubIds(Set.of(TEST_SUBID1)).build();
+            assertEquals(Set.of(TEST_SUBID1), nc2.getSubIds());
+        }
     }
 }
diff --git a/tests/net/common/java/android/net/NetworkProviderTest.kt b/tests/net/common/java/android/net/NetworkProviderTest.kt
index bcc9072..340e6f9 100644
--- a/tests/net/common/java/android/net/NetworkProviderTest.kt
+++ b/tests/net/common/java/android/net/NetworkProviderTest.kt
@@ -27,6 +27,7 @@
 import android.os.Looper
 import androidx.test.InstrumentationRegistry
 import com.android.net.module.util.ArrayTrackRecord
+import com.android.testutils.CompatUtil
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
 import com.android.testutils.DevSdkIgnoreRunner
 import com.android.testutils.isDevSdkInRange
@@ -102,7 +103,8 @@
         mCm.registerNetworkProvider(provider)
         assertNotEquals(provider.getProviderId(), NetworkProvider.ID_NONE)
 
-        val specifier = StringNetworkSpecifier(UUID.randomUUID().toString())
+        val specifier = CompatUtil.makeTestNetworkSpecifier(
+                UUID.randomUUID().toString())
         val nr: NetworkRequest = NetworkRequest.Builder()
                 .addTransportType(TRANSPORT_TEST)
                 .setNetworkSpecifier(specifier)
@@ -183,7 +185,8 @@
 
         mCm.registerNetworkProvider(provider)
 
-        val specifier = StringNetworkSpecifier(UUID.randomUUID().toString())
+        val specifier = CompatUtil.makeTestNetworkSpecifier(
+                UUID.randomUUID().toString())
         val nr: NetworkRequest = NetworkRequest.Builder()
                 .addTransportType(TRANSPORT_TEST)
                 .setNetworkSpecifier(specifier)
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index 2a2dc56..b6e4274 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -44,12 +44,10 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.ConnectivityService
-import com.android.server.LocalServices
 import com.android.server.NetworkAgentWrapper
 import com.android.server.TestNetIdManager
 import com.android.server.connectivity.MockableSystemProperties
 import com.android.server.connectivity.ProxyTracker
-import com.android.server.net.NetworkPolicyManagerInternal
 import com.android.testutils.TestableNetworkCallback
 import org.junit.After
 import org.junit.Before
@@ -75,7 +73,7 @@
 import kotlin.test.fail
 
 const val SERVICE_BIND_TIMEOUT_MS = 5_000L
-const val TEST_TIMEOUT_MS = 1_000L
+const val TEST_TIMEOUT_MS = 10_000L
 
 /**
  * Test that exercises an instrumented version of ConnectivityService against an instrumented
@@ -162,10 +160,6 @@
         networkStackClient.init()
         networkStackClient.start()
 
-        LocalServices.removeServiceForTest(NetworkPolicyManagerInternal::class.java)
-        LocalServices.addService(NetworkPolicyManagerInternal::class.java,
-                mock(NetworkPolicyManagerInternal::class.java))
-
         service = TestConnectivityService(makeDependencies())
         cm = ConnectivityManager(context, service)
         context.addMockSystemService(Context.CONNECTIVITY_SERVICE, cm)
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index e1da3d0..e2d43cb 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -44,11 +44,11 @@
 import android.net.NetworkSpecifier;
 import android.net.QosFilter;
 import android.net.SocketKeepalive;
-import android.net.UidRange;
 import android.os.ConditionVariable;
 import android.os.HandlerThread;
 import android.os.Message;
 import android.util.Log;
+import android.util.Range;
 
 import com.android.net.module.util.ArrayTrackRecord;
 import com.android.server.connectivity.ConnectivityConstants;
@@ -64,6 +64,7 @@
     private final HandlerThread mHandlerThread;
     private final Context mContext;
     private final String mLogTag;
+    private final NetworkAgentConfig mNetworkAgentConfig;
 
     private final ConditionVariable mDisconnected = new ConditionVariable();
     private final ConditionVariable mPreventReconnectReceived = new ConditionVariable();
@@ -115,13 +116,19 @@
         mHandlerThread = new HandlerThread(mLogTag);
         mHandlerThread.start();
 
-        mNetworkAgent = makeNetworkAgent(linkProperties, type, typeName);
+        // extraInfo is set to "" by default in NetworkAgentConfig.
+        final String extraInfo = (transport == TRANSPORT_CELLULAR) ? "internet.apn" : "";
+        mNetworkAgentConfig = new NetworkAgentConfig.Builder()
+                .setLegacyType(type)
+                .setLegacyTypeName(typeName)
+                .setLegacyExtraInfo(extraInfo)
+                .build();
+        mNetworkAgent = makeNetworkAgent(linkProperties, mNetworkAgentConfig);
     }
 
     protected InstrumentedNetworkAgent makeNetworkAgent(LinkProperties linkProperties,
-            final int type, final String typeName)
-            throws Exception {
-        return new InstrumentedNetworkAgent(this, linkProperties, type, typeName);
+            final NetworkAgentConfig nac) throws Exception {
+        return new InstrumentedNetworkAgent(this, linkProperties, nac);
     }
 
     public static class InstrumentedNetworkAgent extends NetworkAgent {
@@ -129,11 +136,9 @@
         private static final String PROVIDER_NAME = "InstrumentedNetworkAgentProvider";
 
         public InstrumentedNetworkAgent(NetworkAgentWrapper wrapper, LinkProperties lp,
-                final int type, final String typeName) {
+                NetworkAgentConfig nac) {
             super(wrapper.mContext, wrapper.mHandlerThread.getLooper(), wrapper.mLogTag,
-                    wrapper.mNetworkCapabilities, lp, wrapper.mScore,
-                    new NetworkAgentConfig.Builder()
-                            .setLegacyType(type).setLegacyTypeName(typeName).build(),
+                    wrapper.mNetworkCapabilities, lp, wrapper.mScore, nac,
                     new NetworkProvider(wrapper.mContext, wrapper.mHandlerThread.getLooper(),
                             PROVIDER_NAME));
             mWrapper = wrapper;
@@ -217,7 +222,7 @@
         mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
     }
 
-    public void setUids(Set<UidRange> uids) {
+    public void setUids(Set<Range<Integer>> uids) {
         mNetworkCapabilities.setUids(uids);
         mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
     }
@@ -301,6 +306,14 @@
         return mNetworkCapabilities;
     }
 
+    public int getLegacyType() {
+        return mNetworkAgentConfig.getLegacyType();
+    }
+
+    public String getExtraInfo() {
+        return mNetworkAgentConfig.getLegacyExtraInfo();
+    }
+
     public @NonNull ArrayTrackRecord<CallbackType>.ReadHead getCallbackHistory() {
         return mCallbackHistory;
     }
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index 6a09b02..6cbdd25 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -41,10 +41,10 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -64,6 +64,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
+import android.os.Process;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -219,8 +220,8 @@
         ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class);
 
         // register callback
-        when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(),
-                any(), nullable(String.class))).thenReturn(request);
+        when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(),
+                anyInt(), anyInt(), any(), nullable(String.class))).thenReturn(request);
         manager.requestNetwork(request, callback, handler);
 
         // callback triggers
@@ -247,8 +248,8 @@
         ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class);
 
         // register callback
-        when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(),
-                any(), nullable(String.class))).thenReturn(req1);
+        when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(),
+                anyInt(), anyInt(), any(), nullable(String.class))).thenReturn(req1);
         manager.requestNetwork(req1, callback, handler);
 
         // callback triggers
@@ -265,8 +266,8 @@
         verify(callback, timeout(100).times(0)).onLosing(any(), anyInt());
 
         // callback can be registered again
-        when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(),
-                any(), nullable(String.class))).thenReturn(req2);
+        when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(),
+                anyInt(), anyInt(), any(), nullable(String.class))).thenReturn(req2);
         manager.requestNetwork(req2, callback, handler);
 
         // callback triggers
@@ -289,8 +290,8 @@
         info.targetSdkVersion = VERSION_CODES.N_MR1 + 1;
 
         when(mCtx.getApplicationInfo()).thenReturn(info);
-        when(mService.requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(), any(),
-                nullable(String.class))).thenReturn(request);
+        when(mService.requestNetwork(anyInt(), any(), anyInt(), any(), anyInt(), any(), anyInt(),
+                anyInt(), any(), nullable(String.class))).thenReturn(request);
 
         Handler handler = new Handler(Looper.getMainLooper());
         manager.requestNetwork(request, callback, handler);
@@ -357,35 +358,41 @@
         final NetworkCallback callback = new ConnectivityManager.NetworkCallback();
 
         manager.requestNetwork(request, callback);
-        verify(mService).requestNetwork(eq(request.networkCapabilities),
-                eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
+        verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(request.networkCapabilities),
+                eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
                 eq(testPkgName), eq(testAttributionTag));
         reset(mService);
 
         // Verify that register network callback does not calls requestNetwork at all.
         manager.registerNetworkCallback(request, callback);
-        verify(mService, never()).requestNetwork(any(), anyInt(), any(), anyInt(), any(),
-                anyInt(), any(), any());
-        verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(),
-                eq(testPkgName), eq(testAttributionTag));
-        reset(mService);
-
-        manager.registerDefaultNetworkCallback(callback);
-        verify(mService).requestNetwork(eq(null),
-                eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
-                eq(testPkgName), eq(testAttributionTag));
-        reset(mService);
-
-        manager.requestBackgroundNetwork(request, null, callback);
-        verify(mService).requestNetwork(eq(request.networkCapabilities),
-                eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
+        verify(mService, never()).requestNetwork(anyInt(), any(), anyInt(), any(), anyInt(), any(),
+                anyInt(), anyInt(), any(), any());
+        verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(), anyInt(),
                 eq(testPkgName), eq(testAttributionTag));
         reset(mService);
 
         Handler handler = new Handler(ConnectivityThread.getInstanceLooper());
+
+        manager.registerDefaultNetworkCallback(callback);
+        verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(null),
+                eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
+                eq(testPkgName), eq(testAttributionTag));
+        reset(mService);
+
+        manager.registerDefaultNetworkCallbackAsUid(42, callback, handler);
+        verify(mService).requestNetwork(eq(42), eq(null),
+                eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
+                eq(testPkgName), eq(testAttributionTag));
+
+        manager.requestBackgroundNetwork(request, handler, callback);
+        verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(request.networkCapabilities),
+                eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
+                eq(testPkgName), eq(testAttributionTag));
+        reset(mService);
+
         manager.registerSystemDefaultNetworkCallback(callback, handler);
-        verify(mService).requestNetwork(eq(null),
-                eq(TRACK_SYSTEM_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
+        verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(null),
+                eq(TRACK_SYSTEM_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
                 eq(testPkgName), eq(testAttributionTag));
         reset(mService);
     }
diff --git a/tests/net/java/android/net/VpnTransportInfoTest.java b/tests/net/java/android/net/VpnTransportInfoTest.java
index d04c87b..b7a42ec 100644
--- a/tests/net/java/android/net/VpnTransportInfoTest.java
+++ b/tests/net/java/android/net/VpnTransportInfoTest.java
@@ -42,7 +42,13 @@
         VpnTransportInfo v1 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM);
         VpnTransportInfo v2 = new VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE);
         VpnTransportInfo v3 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM);
+        VpnTransportInfo v4 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY);
+        VpnTransportInfo v5 = new VpnTransportInfo(VpnManager.TYPE_VPN_OEM);
+
         assertNotEquals(v1, v2);
+        assertNotEquals(v3, v4);
+        assertNotEquals(v4, v5);
+
         assertEquals(v1, v3);
         assertEquals(v1.hashCode(), v3.hashCode());
     }
diff --git a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt b/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
index 8ea226d..b62bdbc 100644
--- a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
+++ b/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.content.res.Resources
+import android.net.ConnectivityResources
 import android.net.NetworkCapabilities
 import android.net.NetworkCapabilities.MAX_TRANSPORT
 import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
@@ -26,13 +27,15 @@
 import android.net.NetworkCapabilities.TRANSPORT_WIFI
 import androidx.test.filters.SmallTest
 import com.android.internal.R
+import org.junit.After
 import org.junit.Assert.assertArrayEquals
 import org.junit.Assert.assertEquals
 import org.junit.Assert.fail
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.any
 import org.mockito.Mockito.doReturn
 import org.mockito.Mockito.mock
 
@@ -47,21 +50,33 @@
 class KeepaliveUtilsTest {
 
     // Prepare mocked context with given resource strings.
-    private fun getMockedContextWithStringArrayRes(id: Int, res: Array<out String?>?): Context {
+    private fun getMockedContextWithStringArrayRes(
+        id: Int,
+        name: String,
+        res: Array<out String?>?
+    ): Context {
         val mockRes = mock(Resources::class.java)
-        doReturn(res).`when`(mockRes).getStringArray(ArgumentMatchers.eq(id))
+        doReturn(res).`when`(mockRes).getStringArray(eq(id))
+        doReturn(id).`when`(mockRes).getIdentifier(eq(name), any(), any())
 
         return mock(Context::class.java).apply {
             doReturn(mockRes).`when`(this).getResources()
+            ConnectivityResources.setResourcesContextForTest(this)
         }
     }
 
+    @After
+    fun tearDown() {
+        ConnectivityResources.setResourcesContextForTest(null)
+    }
+
     @Test
     fun testGetSupportedKeepalives() {
         fun assertRunWithException(res: Array<out String?>?) {
             try {
                 val mockContext = getMockedContextWithStringArrayRes(
-                        R.array.config_networkSupportedKeepaliveCount, res)
+                        R.array.config_networkSupportedKeepaliveCount,
+                        "config_networkSupportedKeepaliveCount", res)
                 KeepaliveUtils.getSupportedKeepalives(mockContext)
                 fail("Expected KeepaliveDeviceConfigurationException")
             } catch (expected: KeepaliveUtils.KeepaliveDeviceConfigurationException) {
@@ -89,7 +104,8 @@
         val expectedValidRes = intArrayOf(3, 0, 0, 0, 4, 0, 0, 0)
 
         val mockContext = getMockedContextWithStringArrayRes(
-                R.array.config_networkSupportedKeepaliveCount, validRes)
+                R.array.config_networkSupportedKeepaliveCount,
+                "config_networkSupportedKeepaliveCount", validRes)
         val actual = KeepaliveUtils.getSupportedKeepalives(mockContext)
         assertArrayEquals(expectedValidRes, actual)
     }
diff --git a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt b/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt
index c1315f6..25aa626 100644
--- a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt
+++ b/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt
@@ -21,18 +21,20 @@
 import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER
 import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE
 import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY
+import android.net.ConnectivityResources
+import android.net.ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI
+import android.net.ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE
 import android.net.util.MultinetworkPolicyTracker.ActiveDataSubscriptionIdListener
 import android.provider.Settings
-import android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI
-import android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE
 import android.telephony.SubscriptionInfo
 import android.telephony.SubscriptionManager
 import android.telephony.TelephonyManager
 import android.test.mock.MockContentResolver
 import androidx.test.filters.SmallTest
 import androidx.test.runner.AndroidJUnit4
-import com.android.internal.R
+import com.android.connectivity.resources.R
 import com.android.internal.util.test.FakeSettingsProvider
+import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
@@ -41,6 +43,7 @@
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.argThat
+import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mockito.any
 import org.mockito.Mockito.doReturn
 import org.mockito.Mockito.mock
@@ -57,6 +60,8 @@
 @SmallTest
 class MultinetworkPolicyTrackerTest {
     private val resources = mock(Resources::class.java).also {
+        doReturn(R.integer.config_networkAvoidBadWifi).`when`(it).getIdentifier(
+                eq("config_networkAvoidBadWifi"), eq("integer"), any())
         doReturn(0).`when`(it).getInteger(R.integer.config_networkAvoidBadWifi)
     }
     private val telephonyManager = mock(TelephonyManager::class.java)
@@ -75,6 +80,7 @@
         doReturn(resources).`when`(it).resources
         doReturn(it).`when`(it).createConfigurationContext(any())
         Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "1")
+        ConnectivityResources.setResourcesContextForTest(it)
     }
     private val tracker = MultinetworkPolicyTracker(context, null /* handler */)
 
@@ -85,6 +91,11 @@
         assertEquals(preference, tracker.meteredMultipathPreference)
     }
 
+    @After
+    fun tearDown() {
+        ConnectivityResources.setResourcesContextForTest(null)
+    }
+
     @Test
     fun testUpdateMeteredMultipathPreference() {
         assertMultipathPreference(MULTIPATH_PREFERENCE_HANDOVER)
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index a643bc8..10b2f1e 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -23,23 +23,31 @@
 import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.ACTION_USER_UNLOCKED;
 import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.FEATURE_WIFI;
+import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED;
+import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER;
+import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
 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.NETID_UNSET;
 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;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
 import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
 import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
+import static android.net.ConnectivityManager.TYPE_PROXY;
 import static android.net.ConnectivityManager.TYPE_VPN;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS;
@@ -77,15 +85,15 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_XCAP;
+import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION;
+import static android.net.NetworkCapabilities.REDACT_FOR_LOCAL_MAC_ADDRESS;
+import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
+import static android.net.NetworkCapabilities.REDACT_NONE;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
-import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
-import static android.net.NetworkPolicyManager.RULE_NONE;
-import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
-import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID;
 import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK;
 import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY;
@@ -174,6 +182,8 @@
 import android.net.ConnectivityManager.PacketKeepalive;
 import android.net.ConnectivityManager.PacketKeepaliveCallback;
 import android.net.ConnectivityManager.TooManyRequestsException;
+import android.net.ConnectivityResources;
+import android.net.ConnectivitySettingsManager;
 import android.net.ConnectivityThread;
 import android.net.DataStallReportParcelable;
 import android.net.EthernetManager;
@@ -182,8 +192,7 @@
 import android.net.INetd;
 import android.net.INetworkMonitor;
 import android.net.INetworkMonitorCallbacks;
-import android.net.INetworkPolicyListener;
-import android.net.IOnSetOemNetworkPreferenceListener;
+import android.net.IOnCompleteListener;
 import android.net.IQosCallback;
 import android.net.InetAddresses;
 import android.net.InterfaceConfigurationParcel;
@@ -201,7 +210,9 @@
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkPolicyManager;
+import android.net.NetworkPolicyManager.NetworkPolicyCallback;
 import android.net.NetworkRequest;
+import android.net.NetworkScore;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
 import android.net.NetworkStackClient;
@@ -229,7 +240,6 @@
 import android.net.shared.NetworkMonitorUtils;
 import android.net.shared.PrivateDnsConfig;
 import android.net.util.MultinetworkPolicyTracker;
-import android.net.wifi.WifiInfo;
 import android.os.BadParcelableException;
 import android.os.Binder;
 import android.os.Build;
@@ -255,18 +265,20 @@
 import android.system.Os;
 import android.telephony.TelephonyManager;
 import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.telephony.data.NrQosSessionAttributes;
 import android.test.mock.MockContentResolver;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
+import android.util.Range;
 import android.util.SparseArray;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.app.IBatteryStats;
+import com.android.connectivity.resources.R;
 import com.android.internal.net.VpnConfig;
 import com.android.internal.net.VpnProfile;
 import com.android.internal.util.ArrayUtils;
@@ -285,7 +297,6 @@
 import com.android.server.connectivity.Vpn;
 import com.android.server.connectivity.VpnProfileStore;
 import com.android.server.net.NetworkPinner;
-import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.testutils.ExceptionUtils;
 import com.android.testutils.HandlerUtils;
 import com.android.testutils.RecorderCallback.CallbackEntry;
@@ -377,6 +388,11 @@
     // Set a non-zero value to verify the flow to set tcp init rwnd value.
     private static final int TEST_TCP_INIT_RWND = 60;
 
+    // Used for testing the per-work-profile default network.
+    private static final int TEST_APP_ID = 103;
+    private static final int TEST_WORK_PROFILE_USER_ID = 2;
+    private static final int TEST_WORK_PROFILE_APP_UID =
+            UserHandle.getUid(TEST_WORK_PROFILE_USER_ID, TEST_APP_ID);
     private static final String CLAT_PREFIX = "v4-";
     private static final String MOBILE_IFNAME = "test_rmnet_data0";
     private static final String WIFI_IFNAME = "test_wlan0";
@@ -411,7 +427,7 @@
     private TestNetworkAgentWrapper mEthernetNetworkAgent;
     private MockVpn mMockVpn;
     private Context mContext;
-    private INetworkPolicyListener mPolicyListener;
+    private NetworkPolicyCallback mPolicyCallback;
     private WrappedMultinetworkPolicyTracker mPolicyTracker;
     private HandlerThread mAlarmManagerThread;
     private TestNetIdManager mNetIdManager;
@@ -420,15 +436,14 @@
     private VpnManagerService mVpnManagerService;
     private TestNetworkCallback mDefaultNetworkCallback;
     private TestNetworkCallback mSystemDefaultNetworkCallback;
+    private TestNetworkCallback mProfileDefaultNetworkCallback;
 
     // State variables required to emulate NetworkPolicyManagerService behaviour.
-    private int mUidRules = RULE_NONE;
-    private boolean mRestrictBackground = false;
+    private int mBlockedReasons = BLOCKED_REASON_NONE;
 
     @Mock DeviceIdleInternal mDeviceIdleInternal;
     @Mock INetworkManagementService mNetworkManagementService;
     @Mock NetworkStatsManager mStatsManager;
-    @Mock IBatteryStats mBatteryStatsService;
     @Mock IDnsResolver mMockDnsResolver;
     @Mock INetd mMockNetd;
     @Mock NetworkStackClient mNetworkStack;
@@ -446,6 +461,7 @@
     @Mock NetworkPolicyManager mNetworkPolicyManager;
     @Mock VpnProfileStore mVpnProfileStore;
     @Mock SystemConfigManager mSystemConfigManager;
+    @Mock Resources mResources;
 
     private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
             ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -473,7 +489,7 @@
     private class MockContext extends BroadcastInterceptingContext {
         private final MockContentResolver mContentResolver;
 
-        @Spy private Resources mResources;
+        @Spy private Resources mInternalResources;
         private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>();
 
         // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant
@@ -482,21 +498,15 @@
         MockContext(Context base, ContentProvider settingsProvider) {
             super(base);
 
-            mResources = spy(base.getResources());
-            when(mResources.getStringArray(com.android.internal.R.array.networkAttributes)).
-                    thenReturn(new String[] {
+            mInternalResources = spy(base.getResources());
+            when(mInternalResources.getStringArray(com.android.internal.R.array.networkAttributes))
+                    .thenReturn(new String[] {
                             "wifi,1,1,1,-1,true",
                             "mobile,0,0,0,-1,true",
                             "mobile_mms,2,0,2,60000,true",
                             "mobile_supl,3,0,2,60000,true",
                     });
 
-            when(mResources.getStringArray(
-                    com.android.internal.R.array.config_wakeonlan_supported_interfaces))
-                    .thenReturn(new String[]{
-                            WIFI_WOL_IFNAME,
-                    });
-
             mContentResolver = new MockContentResolver();
             mContentResolver.addProvider(Settings.AUTHORITY, settingsProvider);
         }
@@ -547,13 +557,26 @@
             return super.getSystemService(name);
         }
 
+        final HashMap<UserHandle, UserManager> mUserManagers = new HashMap<>();
         @Override
         public Context createContextAsUser(UserHandle user, int flags) {
             final Context asUser = mock(Context.class, AdditionalAnswers.delegatesTo(this));
             doReturn(user).when(asUser).getUser();
+            doAnswer((inv) -> {
+                final UserManager um = mUserManagers.computeIfAbsent(user,
+                        u -> mock(UserManager.class, AdditionalAnswers.delegatesTo(mUserManager)));
+                return um;
+            }).when(asUser).getSystemService(Context.USER_SERVICE);
             return asUser;
         }
 
+        public void setWorkProfile(@NonNull final UserHandle userHandle, boolean value) {
+            // This relies on all contexts for a given user returning the same UM mock
+            final UserManager umMock = createContextAsUser(userHandle, 0 /* flags */)
+                    .getSystemService(UserManager.class);
+            doReturn(value).when(umMock).isManagedProfile();
+        }
+
         @Override
         public ContentResolver getContentResolver() {
             return mContentResolver;
@@ -561,7 +584,7 @@
 
         @Override
         public Resources getResources() {
-            return mResources;
+            return mInternalResources;
         }
 
         @Override
@@ -720,7 +743,7 @@
 
         @Override
         protected InstrumentedNetworkAgent makeNetworkAgent(LinkProperties linkProperties,
-                final int type, final String typeName) throws Exception {
+                NetworkAgentConfig nac) throws Exception {
             mNetworkMonitor = mock(INetworkMonitor.class);
 
             final Answer validateAnswer = inv -> {
@@ -739,8 +762,8 @@
                     any() /* name */,
                     nmCbCaptor.capture());
 
-            final InstrumentedNetworkAgent na = new InstrumentedNetworkAgent(this, linkProperties,
-                    type, typeName) {
+            final InstrumentedNetworkAgent na =
+                    new InstrumentedNetworkAgent(this, linkProperties, nac) {
                 @Override
                 public void networkStatus(int status, String redirectUrl) {
                     mRedirectUrl = redirectUrl;
@@ -1084,6 +1107,10 @@
         public void triggerUnfulfillable(NetworkRequest r) {
             super.releaseRequestAsUnfulfillableByAnyFactory(r);
         }
+
+        public void assertNoRequestChanged() {
+            assertNull(mRequestHistory.poll(0, r -> true));
+        }
     }
 
     private Set<UidRange> uidRangesForUids(int... uids) {
@@ -1135,7 +1162,7 @@
         }
 
         public void setUids(Set<UidRange> uids) {
-            mNetworkCapabilities.setUids(uids);
+            mNetworkCapabilities.setUids(UidRange.toIntRanges(uids));
             if (mAgentRegistered) {
                 mMockNetworkAgent.setNetworkCapabilities(mNetworkCapabilities, true);
             }
@@ -1151,11 +1178,6 @@
         }
 
         @Override
-        public int getNetId() {
-            return (mMockNetworkAgent == null) ? NETID_UNSET : mMockNetworkAgent.getNetwork().netId;
-        }
-
-        @Override
         public int getActiveVpnType() {
             return mVpnType;
         }
@@ -1179,10 +1201,10 @@
                     mNetworkCapabilities);
             mMockNetworkAgent.waitForIdle(TIMEOUT_MS);
 
-            verify(mMockNetd, times(1)).networkAddUidRanges(eq(mMockVpn.getNetId()),
+            verify(mMockNetd, times(1)).networkAddUidRanges(eq(mMockVpn.getNetwork().getNetId()),
                     eq(toUidRangeStableParcels(uids)));
             verify(mMockNetd, never())
-                    .networkRemoveUidRanges(eq(mMockVpn.getNetId()), any());
+                    .networkRemoveUidRanges(eq(mMockVpn.getNetwork().getNetId()), any());
             mAgentRegistered = true;
             updateState(NetworkInfo.DetailedState.CONNECTED, "registerAgent");
             mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
@@ -1351,28 +1373,13 @@
     }
 
     private void mockUidNetworkingBlocked() {
-        doAnswer(i -> mContext.getSystemService(NetworkPolicyManager.class)
-                .checkUidNetworkingBlocked(i.getArgument(0) /* uid */, mUidRules,
-                        i.getArgument(1) /* metered */, mRestrictBackground)
+        doAnswer(i -> NetworkPolicyManager.isUidBlocked(mBlockedReasons, i.getArgument(1))
         ).when(mNetworkPolicyManager).isUidNetworkingBlocked(anyInt(), anyBoolean());
-
-        doAnswer(inv -> mContext.getSystemService(NetworkPolicyManager.class)
-                .checkUidNetworkingBlocked(inv.getArgument(0) /* uid */,
-                        inv.getArgument(1) /* uidRules */,
-                        inv.getArgument(2) /* isNetworkMetered */,
-                        inv.getArgument(3) /* isBackgroundRestricted */)
-        ).when(mNetworkPolicyManager).checkUidNetworkingBlocked(
-                anyInt(), anyInt(), anyBoolean(), anyBoolean());
     }
 
-    private void setUidRulesChanged(int uidRules) throws RemoteException {
-        mUidRules = uidRules;
-        mPolicyListener.onUidRulesChanged(Process.myUid(), mUidRules);
-    }
-
-    private void setRestrictBackgroundChanged(boolean restrictBackground) throws RemoteException {
-        mRestrictBackground = restrictBackground;
-        mPolicyListener.onRestrictBackgroundChanged(mRestrictBackground);
+    private void setBlockedReasonChanged(int blockedReasons) {
+        mBlockedReasons = blockedReasons;
+        mPolicyCallback.onUidBlockedReasonChanged(Process.myUid(), blockedReasons);
     }
 
     private Nat464Xlat getNat464Xlat(NetworkAgentWrapper mna) {
@@ -1409,18 +1416,56 @@
         fail("ConditionVariable was blocked for more than " + TIMEOUT_MS + "ms");
     }
 
-    private void registerNetworkCallbackAsUid(NetworkRequest request, NetworkCallback callback,
-            int uid) {
+    private <T> T doAsUid(final int uid, @NonNull final Supplier<T> what) {
         when(mDeps.getCallingUid()).thenReturn(uid);
         try {
-            mCm.registerNetworkCallback(request, callback);
-            waitForIdle();
+            return what.get();
         } finally {
             returnRealCallingUid();
         }
     }
 
+    private void doAsUid(final int uid, @NonNull final Runnable what) {
+        doAsUid(uid, () -> {
+            what.run(); return Void.TYPE;
+        });
+    }
+
+    private void registerNetworkCallbackAsUid(NetworkRequest request, NetworkCallback callback,
+            int uid) {
+        doAsUid(uid, () -> {
+            mCm.registerNetworkCallback(request, callback);
+        });
+    }
+
+    private void registerDefaultNetworkCallbackAsUid(@NonNull final NetworkCallback callback,
+            final int uid) {
+        doAsUid(uid, () -> {
+            mCm.registerDefaultNetworkCallback(callback);
+            waitForIdle();
+        });
+    }
+
+    private interface ExceptionalRunnable {
+        void run() throws Exception;
+    }
+
+    private void withPermission(String permission, ExceptionalRunnable r) throws Exception {
+        if (mServiceContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
+            r.run();
+            return;
+        }
+        try {
+            mServiceContext.setPermission(permission, PERMISSION_GRANTED);
+            r.run();
+        } finally {
+            mServiceContext.setPermission(permission, PERMISSION_DENIED);
+        }
+    }
+
     private static final int PRIMARY_USER = 0;
+    private static final UidRange PRIMARY_UIDRANGE =
+            UidRange.createForUser(UserHandle.of(PRIMARY_USER));
     private static final int APP1_UID = UserHandle.getUid(PRIMARY_USER, 10100);
     private static final int APP2_UID = UserHandle.getUid(PRIMARY_USER, 10101);
     private static final int VPN_UID = UserHandle.getUid(PRIMARY_USER, 10043);
@@ -1456,6 +1501,8 @@
         applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
         when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
                 .thenReturn(applicationInfo);
+        when(mPackageManager.getTargetSdkVersion(anyString()))
+                .thenReturn(applicationInfo.targetSdkVersion);
         when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]);
 
         // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
@@ -1464,14 +1511,14 @@
             Looper.prepare();
         }
         mockDefaultPackages();
+        mockHasSystemFeature(FEATURE_WIFI, true);
+        mockHasSystemFeature(FEATURE_WIFI_DIRECT, true);
+        doReturn(true).when(mTelephonyManager).isDataCapable();
 
         FakeSettingsProvider.clearSettingsProvider();
         mServiceContext = new MockContext(InstrumentationRegistry.getContext(),
                 new FakeSettingsProvider());
         mServiceContext.setUseRegisteredHandlers(true);
-        LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
-        LocalServices.addService(
-                NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class));
 
         mAlarmManagerThread = new HandlerThread("TestAlarmManager");
         mAlarmManagerThread.start();
@@ -1490,10 +1537,11 @@
         mService.mNascentDelayMs = TEST_NASCENT_DELAY_MS;
         verify(mDeps).makeMultinetworkPolicyTracker(any(), any(), any());
 
-        final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
-                ArgumentCaptor.forClass(INetworkPolicyListener.class);
-        verify(mNetworkPolicyManager).registerListener(policyListenerCaptor.capture());
-        mPolicyListener = policyListenerCaptor.getValue();
+        final ArgumentCaptor<NetworkPolicyCallback> policyCallbackCaptor =
+                ArgumentCaptor.forClass(NetworkPolicyCallback.class);
+        verify(mNetworkPolicyManager).registerNetworkPolicyCallback(any(),
+                policyCallbackCaptor.capture());
+        mPolicyCallback = policyCallbackCaptor.getValue();
 
         // Create local CM before sending system ready so that we can answer
         // getSystemService() correctly.
@@ -1506,7 +1554,7 @@
         mQosCallbackTracker = mock(QosCallbackTracker.class);
 
         // Ensure that the default setting for Captive Portals is used for most tests
-        setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
+        setCaptivePortalMode(ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE_PROMPT);
         setAlwaysOnNetworks(false);
         setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
     }
@@ -1516,23 +1564,47 @@
     }
 
     private ConnectivityService.Dependencies makeDependencies() {
-        doReturn(TEST_TCP_INIT_RWND).when(mSystemProperties)
-                .getInt("net.tcp.default_init_rwnd", 0);
         doReturn(false).when(mSystemProperties).getBoolean("ro.radio.noril", false);
-        doNothing().when(mSystemProperties).setTcpInitRwnd(anyInt());
         final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class);
         doReturn(mCsHandlerThread).when(deps).makeHandlerThread();
         doReturn(mNetIdManager).when(deps).makeNetIdManager();
         doReturn(mNetworkStack).when(deps).getNetworkStack();
         doReturn(mSystemProperties).when(deps).getSystemProperties();
         doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
-        doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt());
-        doReturn(mBatteryStatsService).when(deps).getBatteryStatsService();
+        doReturn(true).when(deps).queryUserAccess(anyInt(), any(), any());
         doAnswer(inv -> {
             mPolicyTracker = new WrappedMultinetworkPolicyTracker(
                     inv.getArgument(0), inv.getArgument(1), inv.getArgument(2));
             return mPolicyTracker;
         }).when(deps).makeMultinetworkPolicyTracker(any(), any(), any());
+        doReturn(true).when(deps).getCellular464XlatEnabled();
+
+        doReturn(60000).when(mResources).getInteger(
+                com.android.connectivity.resources.R.integer.config_networkTransitionTimeout);
+        doReturn("").when(mResources).getString(
+                com.android.connectivity.resources.R.string.config_networkCaptivePortalServerUrl);
+        doReturn(new String[]{ WIFI_WOL_IFNAME }).when(mResources).getStringArray(
+                com.android.connectivity.resources.R.array.config_wakeonlan_supported_interfaces);
+        doReturn(new String[] { "0,1", "1,3" }).when(mResources).getStringArray(
+                com.android.connectivity.resources.R.array.config_networkSupportedKeepaliveCount);
+        doReturn(com.android.connectivity.resources.R.array.config_networkSupportedKeepaliveCount)
+                .when(mResources).getIdentifier(eq("config_networkSupportedKeepaliveCount"),
+                eq("array"), any());
+        doReturn(com.android.connectivity.resources.R.array.network_switch_type_name)
+                .when(mResources).getIdentifier(eq("network_switch_type_name"),
+                eq("array"), any());
+
+        // We don't test the actual notification value strings, so just return an empty array.
+        // It doesn't matter what the values are as long as it's not null.
+        doReturn(new String[0]).when(mResources).getStringArray(R.array.network_switch_type_name);
+
+        final ConnectivityResources connRes = mock(ConnectivityResources.class);
+        doReturn(mResources).when(connRes).get();
+        doReturn(connRes).when(deps).getResources(any());
+
+        final Context mockResContext = mock(Context.class);
+        doReturn(mResources).when(mockResContext).getResources();
+        ConnectivityResources.setResourcesContextForTest(mockResContext);
 
         return deps;
     }
@@ -1566,6 +1638,7 @@
     @After
     public void tearDown() throws Exception {
         unregisterDefaultNetworkCallbacks();
+        maybeTearDownEnterpriseNetwork();
         setAlwaysOnNetworks(false);
         if (mCellNetworkAgent != null) {
             mCellNetworkAgent.disconnect();
@@ -1588,6 +1661,7 @@
         waitForIdle();
 
         FakeSettingsProvider.clearSettingsProvider();
+        ConnectivityResources.setResourcesContextForTest(null);
 
         mCsHandlerThread.quitSafely();
         mAlarmManagerThread.quitSafely();
@@ -1743,11 +1817,29 @@
         return expected;
     }
 
+    private boolean extraInfoInBroadcastHasExpectedNullness(NetworkInfo ni) {
+        final DetailedState state = ni.getDetailedState();
+        if (state == DetailedState.CONNECTED && ni.getExtraInfo() == null) return false;
+        // Expect a null extraInfo if the network is CONNECTING, because a CONNECTIVITY_ACTION
+        // broadcast with a state of CONNECTING only happens due to legacy VPN lockdown, which also
+        // nulls out extraInfo.
+        if (state == DetailedState.CONNECTING && ni.getExtraInfo() != null) return false;
+        // Can't make any assertions about DISCONNECTED broadcasts. When a network actually
+        // disconnects, disconnectAndDestroyNetwork sets its state to DISCONNECTED and its extraInfo
+        // to null. But if the DISCONNECTED broadcast is just simulated by LegacyTypeTracker due to
+        // a network switch, extraInfo will likely be populated.
+        // This is likely a bug in CS, but likely not one we can fix without impacting apps.
+        return true;
+    }
+
     private ExpectedBroadcast expectConnectivityAction(int type, NetworkInfo.DetailedState state) {
-        return registerConnectivityBroadcastThat(1, intent ->
-                type == intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) && state.equals(
-                        ((NetworkInfo) intent.getParcelableExtra(EXTRA_NETWORK_INFO))
-                                .getDetailedState()));
+        return registerConnectivityBroadcastThat(1, intent -> {
+            final int actualType = intent.getIntExtra(EXTRA_NETWORK_TYPE, -1);
+            final NetworkInfo ni = intent.getParcelableExtra(EXTRA_NETWORK_INFO);
+            return type == actualType
+                    && state == ni.getDetailedState()
+                    && extraInfoInBroadcastHasExpectedNullness(ni);
+        });
     }
 
     @Test
@@ -1758,7 +1850,8 @@
         assertTrue(mCm.isNetworkSupported(TYPE_WIFI));
         assertTrue(mCm.isNetworkSupported(TYPE_MOBILE));
         assertTrue(mCm.isNetworkSupported(TYPE_MOBILE_MMS));
-        assertFalse(mCm.isNetworkSupported(TYPE_MOBILE_FOTA));
+        assertTrue(mCm.isNetworkSupported(TYPE_MOBILE_FOTA));
+        assertFalse(mCm.isNetworkSupported(TYPE_PROXY));
 
         // Check that TYPE_ETHERNET is supported. Unlike the asserts above, which only validate our
         // mocks, this assert exercises the ConnectivityService code path that ensures that
@@ -3331,7 +3424,7 @@
                 .addCapability(NET_CAPABILITY_VALIDATED).build();
         mCm.registerNetworkCallback(validatedRequest, validatedCallback);
 
-        setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_AVOID);
+        setCaptivePortalMode(ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE_AVOID);
         // Bring up a network with a captive portal.
         // Expect it to fail to connect and not result in any callbacks.
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -3732,9 +3825,10 @@
             NetworkCapabilities networkCapabilities = new NetworkCapabilities();
             networkCapabilities.addTransportType(TRANSPORT_WIFI)
                     .setNetworkSpecifier(new MatchAllNetworkSpecifier());
-            mService.requestNetwork(networkCapabilities, NetworkRequest.Type.REQUEST.ordinal(),
-                    null, 0, null, ConnectivityManager.TYPE_WIFI, mContext.getPackageName(),
-                    getAttributionTag());
+            mService.requestNetwork(Process.INVALID_UID, networkCapabilities,
+                    NetworkRequest.Type.REQUEST.ordinal(), null, 0, null,
+                    ConnectivityManager.TYPE_WIFI, NetworkCallback.FLAG_NONE,
+                    mContext.getPackageName(), getAttributionTag());
         });
 
         class NonParcelableSpecifier extends NetworkSpecifier {
@@ -3962,7 +4056,7 @@
     }
 
     @Test
-    public void testRegisterSystemDefaultCallbackRequiresNetworkSettings() throws Exception {
+    public void testRegisterPrivilegedDefaultCallbacksRequireNetworkSettings() throws Exception {
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(false /* validated */);
 
@@ -3971,30 +4065,38 @@
         assertThrows(SecurityException.class,
                 () -> mCm.registerSystemDefaultNetworkCallback(callback, handler));
         callback.assertNoCallback();
+        assertThrows(SecurityException.class,
+                () -> mCm.registerDefaultNetworkCallbackAsUid(APP1_UID, callback, handler));
+        callback.assertNoCallback();
 
         mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS,
                 PERMISSION_GRANTED);
         mCm.registerSystemDefaultNetworkCallback(callback, handler);
         callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
         mCm.unregisterNetworkCallback(callback);
+
+        mCm.registerDefaultNetworkCallbackAsUid(APP1_UID, callback, handler);
+        callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+        mCm.unregisterNetworkCallback(callback);
     }
 
     private void setCaptivePortalMode(int mode) {
         ContentResolver cr = mServiceContext.getContentResolver();
-        Settings.Global.putInt(cr, Settings.Global.CAPTIVE_PORTAL_MODE, mode);
+        Settings.Global.putInt(cr, ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE, mode);
     }
 
     private void setAlwaysOnNetworks(boolean enable) {
         ContentResolver cr = mServiceContext.getContentResolver();
-        Settings.Global.putInt(cr, Settings.Global.MOBILE_DATA_ALWAYS_ON, enable ? 1 : 0);
+        Settings.Global.putInt(cr, ConnectivitySettingsManager.MOBILE_DATA_ALWAYS_ON,
+                enable ? 1 : 0);
         mService.updateAlwaysOnNetworks();
         waitForIdle();
     }
 
     private void setPrivateDnsSettings(String mode, String specifier) {
         final ContentResolver cr = mServiceContext.getContentResolver();
-        Settings.Global.putString(cr, Settings.Global.PRIVATE_DNS_MODE, mode);
-        Settings.Global.putString(cr, Settings.Global.PRIVATE_DNS_SPECIFIER, specifier);
+        Settings.Global.putString(cr, ConnectivitySettingsManager.PRIVATE_DNS_MODE, mode);
+        Settings.Global.putString(cr, ConnectivitySettingsManager.PRIVATE_DNS_SPECIFIER, specifier);
         mService.updatePrivateDnsSettings();
         waitForIdle();
     }
@@ -4011,7 +4113,8 @@
         grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid());
         final TestNetworkCallback cellBgCallback = new TestNetworkCallback();
         mCm.requestBackgroundNetwork(new NetworkRequest.Builder()
-                .addTransportType(TRANSPORT_CELLULAR).build(), null, cellBgCallback);
+                .addTransportType(TRANSPORT_CELLULAR).build(),
+                mCsHandlerThread.getThreadHandler(), cellBgCallback);
 
         // Make callbacks for monitoring.
         final NetworkRequest request = new NetworkRequest.Builder().build();
@@ -4231,7 +4334,7 @@
     @Test
     public void testAvoidBadWifiSetting() throws Exception {
         final ContentResolver cr = mServiceContext.getContentResolver();
-        final String settingName = Settings.Global.NETWORK_AVOID_BAD_WIFI;
+        final String settingName = ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI;
 
         mPolicyTracker.mConfigRestrictsAvoidBadWifi = false;
         String[] values = new String[] {null, "0", "1"};
@@ -4265,6 +4368,7 @@
         assertTrue(mPolicyTracker.shouldNotifyWifiUnvalidated());
     }
 
+    @Ignore("Refactoring in progress b/178071397")
     @Test
     public void testAvoidBadWifi() throws Exception {
         final ContentResolver cr = mServiceContext.getContentResolver();
@@ -4288,7 +4392,7 @@
         TestNetworkCallback validatedWifiCallback = new TestNetworkCallback();
         mCm.registerNetworkCallback(validatedWifiRequest, validatedWifiCallback);
 
-        Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 0);
+        Settings.Global.putInt(cr, ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, 0);
         mPolicyTracker.reevaluate();
 
         // Bring up validated cell.
@@ -4356,7 +4460,7 @@
         validatedWifiCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
 
         // Simulate the user selecting "switch" and checking the don't ask again checkbox.
-        Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1);
+        Settings.Global.putInt(cr, ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, 1);
         mPolicyTracker.reevaluate();
 
         // We now switch to cell.
@@ -4369,11 +4473,11 @@
 
         // Simulate the user turning the cellular fallback setting off and then on.
         // We switch to wifi and then to cell.
-        Settings.Global.putString(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
+        Settings.Global.putString(cr, ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, null);
         mPolicyTracker.reevaluate();
         defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         assertEquals(mCm.getActiveNetwork(), wifiNetwork);
-        Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1);
+        Settings.Global.putInt(cr, ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, 1);
         mPolicyTracker.reevaluate();
         defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
         assertEquals(mCm.getActiveNetwork(), cellNetwork);
@@ -4392,7 +4496,7 @@
     @Test
     public void testMeteredMultipathPreferenceSetting() throws Exception {
         final ContentResolver cr = mServiceContext.getContentResolver();
-        final String settingName = Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
+        final String settingName = ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE;
 
         for (int config : Arrays.asList(0, 3, 2)) {
             for (String setting: Arrays.asList(null, "0", "2", "1")) {
@@ -6868,7 +6972,7 @@
         final int uid = Process.myUid();
         NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
         assertNotNull("nc=" + nc, nc.getUids());
-        assertEquals(nc.getUids(), uidRangesForUids(uid));
+        assertEquals(nc.getUids(), UidRange.toIntRanges(uidRangesForUids(uid)));
         assertVpnTransportInfo(nc, VpnManager.TYPE_VPN_SERVICE);
 
         // Set an underlying network and expect to see the VPN transports change.
@@ -6893,10 +6997,13 @@
 
         // Expect that the VPN UID ranges contain both |uid| and the UID range for the newly-added
         // restricted user.
+        final UidRange rRange = UidRange.createForUser(UserHandle.of(RESTRICTED_USER));
+        final Range<Integer> restrictUidRange = new Range<Integer>(rRange.start, rRange.stop);
+        final Range<Integer> singleUidRange = new Range<Integer>(uid, uid);
         callback.expectCapabilitiesThat(mMockVpn, (caps)
                 -> caps.getUids().size() == 2
-                && caps.getUids().contains(new UidRange(uid, uid))
-                && caps.getUids().contains(createUidRange(RESTRICTED_USER))
+                && caps.getUids().contains(singleUidRange)
+                && caps.getUids().contains(restrictUidRange)
                 && caps.hasTransport(TRANSPORT_VPN)
                 && caps.hasTransport(TRANSPORT_WIFI));
 
@@ -6905,8 +7012,8 @@
         callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
         callback.expectCapabilitiesThat(mMockVpn, (caps)
                 -> caps.getUids().size() == 2
-                && caps.getUids().contains(new UidRange(uid, uid))
-                && caps.getUids().contains(createUidRange(RESTRICTED_USER))
+                && caps.getUids().contains(singleUidRange)
+                && caps.getUids().contains(restrictUidRange)
                 && caps.hasTransport(TRANSPORT_VPN)
                 && !caps.hasTransport(TRANSPORT_WIFI));
 
@@ -6920,7 +7027,7 @@
         // change made just before that (i.e., loss of TRANSPORT_WIFI) is preserved.
         callback.expectCapabilitiesThat(mMockVpn, (caps)
                 -> caps.getUids().size() == 1
-                && caps.getUids().contains(new UidRange(uid, uid))
+                && caps.getUids().contains(singleUidRange)
                 && caps.hasTransport(TRANSPORT_VPN)
                 && !caps.hasTransport(TRANSPORT_WIFI));
     }
@@ -7170,6 +7277,20 @@
         mMockVpn.disconnect();
     }
 
+    private class DetailedBlockedStatusCallback extends TestNetworkCallback {
+        public void expectAvailableThenValidatedCallbacks(HasNetwork n, int blockedStatus) {
+            super.expectAvailableThenValidatedCallbacks(n.getNetwork(), blockedStatus, TIMEOUT_MS);
+        }
+        public void expectBlockedStatusCallback(HasNetwork n, int blockedStatus) {
+            // This doesn't work:
+            // super.expectBlockedStatusCallback(blockedStatus, n.getNetwork());
+            super.expectBlockedStatusCallback(blockedStatus, n.getNetwork(), TIMEOUT_MS);
+        }
+        public void onBlockedStatusChanged(Network network, int blockedReasons) {
+            getHistory().add(new CallbackEntry.BlockedStatusInt(network, blockedReasons));
+        }
+    }
+
     @Test
     public void testNetworkBlockedStatus() throws Exception {
         final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
@@ -7177,81 +7298,116 @@
                 .addTransportType(TRANSPORT_CELLULAR)
                 .build();
         mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+        final DetailedBlockedStatusCallback detailedCallback = new DetailedBlockedStatusCallback();
+        mCm.registerNetworkCallback(cellRequest, detailedCallback);
+
         mockUidNetworkingBlocked();
 
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
         cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        detailedCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent,
+                BLOCKED_REASON_NONE);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mCellNetworkAgent);
 
-        setUidRulesChanged(RULE_REJECT_ALL);
+        setBlockedReasonChanged(BLOCKED_REASON_BATTERY_SAVER);
         cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+        detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent,
+                BLOCKED_REASON_BATTERY_SAVER);
         assertNull(mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+        assertExtraInfoFromCmBlocked(mCellNetworkAgent);
 
-        // ConnectivityService should cache it not to invoke the callback again.
-        setUidRulesChanged(RULE_REJECT_METERED);
-        cellNetworkCallback.assertNoCallback();
+        // If blocked state does not change but blocked reason does, the boolean callback is called.
+        // TODO: investigate de-duplicating.
+        setBlockedReasonChanged(BLOCKED_METERED_REASON_USER_RESTRICTED);
+        cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+        detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent,
+                BLOCKED_METERED_REASON_USER_RESTRICTED);
 
-        setUidRulesChanged(RULE_NONE);
+        setBlockedReasonChanged(BLOCKED_REASON_NONE);
         cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+        detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mCellNetworkAgent);
 
-        setUidRulesChanged(RULE_REJECT_METERED);
+        setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER);
         cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+        detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent,
+                BLOCKED_METERED_REASON_DATA_SAVER);
         assertNull(mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+        assertExtraInfoFromCmBlocked(mCellNetworkAgent);
 
         // Restrict the network based on UID rule and NOT_METERED capability change.
         mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
         cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent);
         cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+        detailedCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent);
+        detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mCellNetworkAgent);
 
         mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
         cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED,
                 mCellNetworkAgent);
         cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+        detailedCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED,
+                mCellNetworkAgent);
+        detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent,
+                BLOCKED_METERED_REASON_DATA_SAVER);
         assertNull(mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+        assertExtraInfoFromCmBlocked(mCellNetworkAgent);
 
-        setUidRulesChanged(RULE_ALLOW_METERED);
+        setBlockedReasonChanged(BLOCKED_REASON_NONE);
         cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+        detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mCellNetworkAgent);
 
-        setUidRulesChanged(RULE_NONE);
+        setBlockedReasonChanged(BLOCKED_REASON_NONE);
         cellNetworkCallback.assertNoCallback();
+        detailedCallback.assertNoCallback();
 
         // Restrict background data. Networking is not blocked because the network is unmetered.
-        setRestrictBackgroundChanged(true);
+        setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER);
         cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+        detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent,
+                BLOCKED_METERED_REASON_DATA_SAVER);
         assertNull(mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
-        setRestrictBackgroundChanged(true);
+        assertExtraInfoFromCmBlocked(mCellNetworkAgent);
+        setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER);
         cellNetworkCallback.assertNoCallback();
 
-        setUidRulesChanged(RULE_ALLOW_METERED);
+        setBlockedReasonChanged(BLOCKED_REASON_NONE);
         cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+        detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE);
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mCellNetworkAgent);
 
-        setRestrictBackgroundChanged(false);
+        setBlockedReasonChanged(BLOCKED_REASON_NONE);
         cellNetworkCallback.assertNoCallback();
+        detailedCallback.assertNoCallback();
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mCellNetworkAgent);
 
         mCm.unregisterNetworkCallback(cellNetworkCallback);
     }
@@ -7263,9 +7419,9 @@
         mockUidNetworkingBlocked();
 
         // No Networkcallbacks invoked before any network is active.
-        setUidRulesChanged(RULE_REJECT_ALL);
-        setUidRulesChanged(RULE_NONE);
-        setUidRulesChanged(RULE_REJECT_METERED);
+        setBlockedReasonChanged(BLOCKED_REASON_BATTERY_SAVER);
+        setBlockedReasonChanged(BLOCKED_REASON_NONE);
+        setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER);
         defaultCallback.assertNoCallback();
 
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
@@ -7290,8 +7446,8 @@
         defaultCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
 
         // Verify there's no Networkcallbacks invoked after data saver on/off.
-        setRestrictBackgroundChanged(true);
-        setRestrictBackgroundChanged(false);
+        setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER);
+        setBlockedReasonChanged(BLOCKED_REASON_NONE);
         defaultCallback.assertNoCallback();
 
         mCellNetworkAgent.disconnect();
@@ -7310,6 +7466,15 @@
         assertNotNull(ni);
         assertEquals(type, ni.getType());
         assertEquals(ConnectivityManager.getNetworkTypeName(type), state, ni.getDetailedState());
+        if (state == DetailedState.CONNECTED || state == DetailedState.SUSPENDED) {
+            assertNotNull(ni.getExtraInfo());
+        } else {
+            // Technically speaking, a network that's in CONNECTING state will generally have a
+            // non-null extraInfo. This doesn't actually happen in this test because it never calls
+            // a legacy API while a network is connecting. When a network is in CONNECTING state
+            // because of legacy lockdown VPN, its extraInfo is always null.
+            assertNull(ni.getExtraInfo());
+        }
     }
 
     private void assertActiveNetworkInfo(int type, DetailedState state) {
@@ -7319,6 +7484,26 @@
         checkNetworkInfo(mCm.getNetworkInfo(type), type, state);
     }
 
+    private void assertExtraInfoFromCm(TestNetworkAgentWrapper network, boolean present) {
+        final NetworkInfo niForNetwork = mCm.getNetworkInfo(network.getNetwork());
+        final NetworkInfo niForType = mCm.getNetworkInfo(network.getLegacyType());
+        if (present) {
+            assertEquals(network.getExtraInfo(), niForNetwork.getExtraInfo());
+            assertEquals(network.getExtraInfo(), niForType.getExtraInfo());
+        } else {
+            assertNull(niForNetwork.getExtraInfo());
+            assertNull(niForType.getExtraInfo());
+        }
+    }
+
+    private void assertExtraInfoFromCmBlocked(TestNetworkAgentWrapper network) {
+        assertExtraInfoFromCm(network, false);
+    }
+
+    private void assertExtraInfoFromCmPresent(TestNetworkAgentWrapper network) {
+        assertExtraInfoFromCm(network, true);
+    }
+
     // Checks that each of the |agents| receive a blocked status change callback with the specified
     // |blocked| value, in any order. This is needed because when an event affects multiple
     // networks, ConnectivityService does not guarantee the order in which callbacks are fired.
@@ -7361,6 +7546,13 @@
         final NetworkRequest vpnUidRequest = new NetworkRequest.Builder().build();
         registerNetworkCallbackAsUid(vpnUidRequest, vpnUidCallback, VPN_UID);
 
+        final TestNetworkCallback vpnUidDefaultCallback = new TestNetworkCallback();
+        registerDefaultNetworkCallbackAsUid(vpnUidDefaultCallback, VPN_UID);
+
+        final TestNetworkCallback vpnDefaultCallbackAsUid = new TestNetworkCallback();
+        mCm.registerDefaultNetworkCallbackAsUid(VPN_UID, vpnDefaultCallbackAsUid,
+                new Handler(ConnectivityThread.getInstanceLooper()));
+
         final int uid = Process.myUid();
         final int userId = UserHandle.getUserId(uid);
         final ArrayList<String> allowList = new ArrayList<>();
@@ -7379,6 +7571,8 @@
         callback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent);
         defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent);
         vpnUidCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        vpnUidDefaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        vpnDefaultCallbackAsUid.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
         assertNull(mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
@@ -7391,6 +7585,8 @@
         callback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
         defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
         vpnUidCallback.assertNoCallback();
+        vpnUidDefaultCallback.assertNoCallback();
+        vpnDefaultCallbackAsUid.assertNoCallback();
         expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -7405,6 +7601,8 @@
         callback.assertNoCallback();
         defaultCallback.assertNoCallback();
         vpnUidCallback.assertNoCallback();
+        vpnUidDefaultCallback.assertNoCallback();
+        vpnDefaultCallbackAsUid.assertNoCallback();
 
         // The following requires that the UID of this test package is greater than VPN_UID. This
         // is always true in practice because a plain AOSP build with no apps installed has almost
@@ -7425,6 +7623,8 @@
         callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
         defaultCallback.assertNoCallback();
         vpnUidCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+        vpnUidDefaultCallback.assertNoCallback();
+        vpnDefaultCallbackAsUid.assertNoCallback();
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
@@ -7445,6 +7645,8 @@
         defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent);
         assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent);
         vpnUidCallback.assertNoCallback();
+        vpnUidDefaultCallback.assertNoCallback();
+        vpnDefaultCallbackAsUid.assertNoCallback();
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
         assertNull(mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
@@ -7456,6 +7658,8 @@
         defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
         assertBlockedCallbackInAnyOrder(callback, false, mWiFiNetworkAgent, mCellNetworkAgent);
         vpnUidCallback.assertNoCallback();
+        vpnUidDefaultCallback.assertNoCallback();
+        vpnDefaultCallbackAsUid.assertNoCallback();
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
@@ -7470,6 +7674,8 @@
         callback.assertNoCallback();
         defaultCallback.assertNoCallback();
         vpnUidCallback.assertNoCallback();
+        vpnUidDefaultCallback.assertNoCallback();
+        vpnDefaultCallbackAsUid.assertNoCallback();
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
@@ -7481,6 +7687,8 @@
         callback.assertNoCallback();
         defaultCallback.assertNoCallback();
         vpnUidCallback.assertNoCallback();
+        vpnUidDefaultCallback.assertNoCallback();
+        vpnDefaultCallbackAsUid.assertNoCallback();
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
@@ -7493,6 +7701,8 @@
         defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent);
         assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent);
         vpnUidCallback.assertNoCallback();
+        vpnUidDefaultCallback.assertNoCallback();
+        vpnDefaultCallbackAsUid.assertNoCallback();
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
         assertNull(mCm.getActiveNetwork());
         assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
@@ -7503,6 +7713,8 @@
         assertUidRangesUpdatedForMyUid(true);
         defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
         vpnUidCallback.assertNoCallback();  // vpnUidCallback has NOT_VPN capability.
+        vpnUidDefaultCallback.assertNoCallback();  // VPN does not apply to VPN_UID
+        vpnDefaultCallbackAsUid.assertNoCallback();
         assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
         assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
@@ -7513,11 +7725,16 @@
         mMockVpn.disconnect();
         defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
         defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent);
+        vpnUidCallback.assertNoCallback();
+        vpnUidDefaultCallback.assertNoCallback();
+        vpnDefaultCallbackAsUid.assertNoCallback();
         assertNull(mCm.getActiveNetwork());
 
         mCm.unregisterNetworkCallback(callback);
         mCm.unregisterNetworkCallback(defaultCallback);
         mCm.unregisterNetworkCallback(vpnUidCallback);
+        mCm.unregisterNetworkCallback(vpnUidDefaultCallback);
+        mCm.unregisterNetworkCallback(vpnDefaultCallbackAsUid);
     }
 
     private void setupLegacyLockdownVpn() {
@@ -7539,7 +7756,7 @@
         assertNotNull(underlying);
         mMockVpn.setVpnType(VpnManager.TYPE_VPN_LEGACY);
         // The legacy lockdown VPN only supports userId 0.
-        final Set<UidRange> ranges = Collections.singleton(createUidRange(PRIMARY_USER));
+        final Set<UidRange> ranges = Collections.singleton(PRIMARY_UIDRANGE);
         mMockVpn.registerAgent(ranges);
         mMockVpn.setUnderlyingNetworks(new Network[]{underlying});
         mMockVpn.connect(true);
@@ -7633,6 +7850,7 @@
         assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED);
+        assertExtraInfoFromCmBlocked(mCellNetworkAgent);
 
         // TODO: it would be nice if we could simply rely on the production code here, and have
         // LockdownVpnTracker start the VPN, have the VPN code register its NetworkAgent with
@@ -7661,6 +7879,7 @@
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
         assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mCellNetworkAgent);
         assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
         assertTrue(vpnNc.hasTransport(TRANSPORT_CELLULAR));
         assertFalse(vpnNc.hasTransport(TRANSPORT_WIFI));
@@ -7703,6 +7922,7 @@
         assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
         assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED);
+        assertExtraInfoFromCmBlocked(mWiFiNetworkAgent);
 
         // The VPN comes up again on wifi.
         b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
@@ -7717,6 +7937,7 @@
         assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mWiFiNetworkAgent);
         vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
         assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
         assertTrue(vpnNc.hasTransport(TRANSPORT_WIFI));
@@ -7733,6 +7954,7 @@
         assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+        assertExtraInfoFromCmPresent(mWiFiNetworkAgent);
 
         b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED);
         mWiFiNetworkAgent.disconnect();
@@ -7812,7 +8034,6 @@
         verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext,
                 cellLp.getInterfaceName(),
                 new int[] { TRANSPORT_CELLULAR });
-        reset(mBatteryStatsService);
 
         final LinkProperties wifiLp = new LinkProperties();
         wifiLp.setInterfaceName("wifi0");
@@ -7822,7 +8043,6 @@
         verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext,
                 wifiLp.getInterfaceName(),
                 new int[] { TRANSPORT_WIFI });
-        reset(mBatteryStatsService);
 
         mCellNetworkAgent.disconnect();
         mWiFiNetworkAgent.disconnect();
@@ -7905,7 +8125,6 @@
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
         reset(mMockDnsResolver);
         reset(mMockNetd);
-        reset(mBatteryStatsService);
 
         // Connect with ipv6 link properties. Expect prefix discovery to be started.
         mCellNetworkAgent.connect(true);
@@ -7925,7 +8144,6 @@
 
         // Switching default network updates TCP buffer sizes.
         verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES);
-        verify(mSystemProperties, times(1)).setTcpInitRwnd(eq(TEST_TCP_INIT_RWND));
         // Add an IPv4 address. Expect prefix discovery to be stopped. Netd doesn't tell us that
         // the NAT64 prefix was removed because one was never discovered.
         cellLp.addLinkAddress(myIpv4);
@@ -8268,6 +8486,45 @@
     }
 
     @Test
+    public void testWith464XlatDisable() throws Exception {
+        doReturn(false).when(mDeps).getCellular464XlatEnabled();
+
+        final TestNetworkCallback callback = new TestNetworkCallback();
+        final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+        final NetworkRequest networkRequest = new NetworkRequest.Builder()
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .build();
+        mCm.registerNetworkCallback(networkRequest, callback);
+        mCm.registerDefaultNetworkCallback(defaultCallback);
+
+        // Bring up validated cell.
+        final LinkProperties cellLp = new LinkProperties();
+        cellLp.setInterfaceName(MOBILE_IFNAME);
+        cellLp.addLinkAddress(new LinkAddress("2001:db8:1::1/64"));
+        cellLp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, MOBILE_IFNAME));
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+
+        mCellNetworkAgent.sendLinkProperties(cellLp);
+        mCellNetworkAgent.connect(true);
+        callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        final int cellNetId = mCellNetworkAgent.getNetwork().netId;
+        waitForIdle();
+
+        verify(mMockDnsResolver, never()).startPrefix64Discovery(cellNetId);
+        Nat464Xlat clat = getNat464Xlat(mCellNetworkAgent);
+        assertTrue("Nat464Xlat was not IDLE", !clat.isStarted());
+
+        // This cannot happen because prefix discovery cannot succeed if it is never started.
+        mService.mResolverUnsolEventCallback.onNat64PrefixEvent(
+                makeNat64PrefixEvent(cellNetId, PREFIX_OPERATION_ADDED, "64:ff9b::", 96));
+
+        // ... but still, check that even if it did, clatd would not be started.
+        verify(mMockNetd, never()).clatdStart(anyString(), anyString());
+        assertTrue("Nat464Xlat was not IDLE", !clat.isStarted());
+    }
+
+    @Test
     public void testDataActivityTracking() throws Exception {
         final TestNetworkCallback networkCallback = new TestNetworkCallback();
         final NetworkRequest networkRequest = new NetworkRequest.Builder()
@@ -8372,14 +8629,12 @@
         mCellNetworkAgent.connect(false);
         networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
         verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES);
-        verify(mSystemProperties, times(1)).setTcpInitRwnd(eq(TEST_TCP_INIT_RWND));
         // Change link Properties should have updated tcp buffer size.
         LinkProperties lp = new LinkProperties();
         lp.setTcpBufferSizes(testTcpBufferSizes);
         mCellNetworkAgent.sendLinkProperties(lp);
         networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
         verifyTcpBufferSizeChange(testTcpBufferSizes);
-        verify(mSystemProperties, times(2)).setTcpInitRwnd(eq(TEST_TCP_INIT_RWND));
         // Clean up.
         mCellNetworkAgent.disconnect();
         networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
@@ -8463,7 +8718,7 @@
         lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
         // The uid range needs to cover the test app so the network is visible to it.
-        final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER));
+        final Set<UidRange> vpnRange = Collections.singleton(PRIMARY_UIDRANGE);
         mMockVpn.establish(lp, VPN_UID, vpnRange);
         assertVpnUidRangesUpdated(true, vpnRange, VPN_UID);
 
@@ -8491,7 +8746,7 @@
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
         lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
         // The uid range needs to cover the test app so the network is visible to it.
-        final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER));
+        final Set<UidRange> vpnRange = Collections.singleton(PRIMARY_UIDRANGE);
         mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
         assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
 
@@ -8507,7 +8762,7 @@
         lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun0"));
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
         // The uid range needs to cover the test app so the network is visible to it.
-        final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER));
+        final Set<UidRange> vpnRange = Collections.singleton(PRIMARY_UIDRANGE);
         mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
         assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
 
@@ -8522,7 +8777,7 @@
         lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
         // The uid range needs to cover the test app so the network is visible to it.
-        final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER));
+        final Set<UidRange> vpnRange = Collections.singleton(PRIMARY_UIDRANGE);
         mMockVpn.establish(lp, VPN_UID, vpnRange);
         assertVpnUidRangesUpdated(true, vpnRange, VPN_UID);
 
@@ -8574,7 +8829,7 @@
         lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
         // The uid range needs to cover the test app so the network is visible to it.
-        final UidRange vpnRange = createUidRange(PRIMARY_USER);
+        final UidRange vpnRange = PRIMARY_UIDRANGE;
         final Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
         mMockVpn.establish(lp, VPN_UID, vpnRanges);
         assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
@@ -8659,6 +8914,7 @@
         applicationInfo.targetSdkVersion = targetSdk;
         when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
                 .thenReturn(applicationInfo);
+        when(mPackageManager.getTargetSdkVersion(any())).thenReturn(targetSdk);
 
         when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
 
@@ -8673,107 +8929,372 @@
         }
     }
 
-    private int getOwnerUidNetCapsForCallerPermission(int ownerUid, int callerUid) {
+    private int getOwnerUidNetCapsPermission(int ownerUid, int callerUid,
+            boolean includeLocationSensitiveInfo) {
         final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid);
 
         return mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                netCap, callerUid, mContext.getPackageName(), getAttributionTag()).getOwnerUid();
+                netCap, includeLocationSensitiveInfo, Process.myUid(), callerUid,
+                mContext.getPackageName(), getAttributionTag())
+                .getOwnerUid();
     }
 
-    private void verifyWifiInfoCopyNetCapsForCallerPermission(
-            int callerUid, boolean shouldMakeCopyWithLocationSensitiveFieldsParcelable) {
-        final WifiInfo wifiInfo = mock(WifiInfo.class);
-        when(wifiInfo.hasLocationSensitiveFields()).thenReturn(true);
-        final NetworkCapabilities netCap = new NetworkCapabilities().setTransportInfo(wifiInfo);
+    private void verifyTransportInfoCopyNetCapsPermission(
+            int callerUid, boolean includeLocationSensitiveInfo,
+            boolean shouldMakeCopyWithLocationSensitiveFieldsParcelable) {
+        final TransportInfo transportInfo = mock(TransportInfo.class);
+        when(transportInfo.getApplicableRedactions()).thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION);
+        final NetworkCapabilities netCap =
+                new NetworkCapabilities().setTransportInfo(transportInfo);
 
         mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                netCap, callerUid, mContext.getPackageName(), getAttributionTag());
-        verify(wifiInfo).makeCopy(eq(shouldMakeCopyWithLocationSensitiveFieldsParcelable));
+                netCap, includeLocationSensitiveInfo, Process.myPid(), callerUid,
+                mContext.getPackageName(), getAttributionTag());
+        if (shouldMakeCopyWithLocationSensitiveFieldsParcelable) {
+            verify(transportInfo).makeCopy(REDACT_NONE);
+        } else {
+            verify(transportInfo).makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION);
+        }
+    }
+
+    private void verifyOwnerUidAndTransportInfoNetCapsPermission(
+            boolean shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag,
+            boolean shouldInclLocationSensitiveOwnerUidWithIncludeFlag,
+            boolean shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag,
+            boolean shouldInclLocationSensitiveTransportInfoWithIncludeFlag) {
+        final int myUid = Process.myUid();
+
+        final int expectedOwnerUidWithoutIncludeFlag =
+                shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag
+                        ? Process.myUid() : INVALID_UID;
+        assertEquals(expectedOwnerUidWithoutIncludeFlag, getOwnerUidNetCapsPermission(
+                myUid, myUid, false /* includeLocationSensitiveInfo */));
+
+        final int expectedOwnerUidWithIncludeFlag =
+                shouldInclLocationSensitiveOwnerUidWithIncludeFlag ? myUid : INVALID_UID;
+        assertEquals(expectedOwnerUidWithIncludeFlag, getOwnerUidNetCapsPermission(
+                myUid, myUid, true /* includeLocationSensitiveInfo */));
+
+        verifyTransportInfoCopyNetCapsPermission(myUid,
+                false, /* includeLocationSensitiveInfo */
+                shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag);
+
+        verifyTransportInfoCopyNetCapsPermission(myUid,
+                true, /* includeLocationSensitiveInfo */
+                shouldInclLocationSensitiveTransportInfoWithIncludeFlag);
+
     }
 
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedWithFineLocationAfterQ()
+    public void testCreateWithLocationInfoSanitizedWithFineLocationAfterQ()
             throws Exception {
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
-        final int myUid = Process.myUid();
-        assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        verifyOwnerUidAndTransportInfoNetCapsPermission(
+                // Ensure that we include owner uid even if the request asks to remove it since the
+                // app has necessary permissions and targetSdk < S.
+                true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
+                // Ensure that we remove location info if the request asks to remove it even if the
+                // app has necessary permissions.
+                true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
+        );
     }
 
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedWithCoarseLocationPreQ()
+    public void testCreateWithLocationInfoSanitizedWithFineLocationPreSWithAndWithoutCallbackFlag()
+            throws Exception {
+        setupLocationPermissions(Build.VERSION_CODES.R, true, AppOpsManager.OPSTR_FINE_LOCATION,
+                Manifest.permission.ACCESS_FINE_LOCATION);
+
+        verifyOwnerUidAndTransportInfoNetCapsPermission(
+                // Ensure that we include owner uid even if the request asks to remove it since the
+                // app has necessary permissions and targetSdk < S.
+                true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
+                // Ensure that we remove location info if the request asks to remove it even if the
+                // app has necessary permissions.
+                true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
+        );
+    }
+
+    @Test
+    public void
+            testCreateWithLocationInfoSanitizedWithFineLocationAfterSWithAndWithoutCallbackFlag()
+            throws Exception {
+        setupLocationPermissions(Build.VERSION_CODES.S, true, AppOpsManager.OPSTR_FINE_LOCATION,
+                Manifest.permission.ACCESS_FINE_LOCATION);
+
+        verifyOwnerUidAndTransportInfoNetCapsPermission(
+                // Ensure that we owner UID if the request asks us to remove it even if the app
+                // has necessary permissions since targetSdk >= S.
+                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
+                // Ensure that we remove location info if the request asks to remove it even if the
+                // app has necessary permissions.
+                true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
+        );
+    }
+
+    @Test
+    public void testCreateWithLocationInfoSanitizedWithCoarseLocationPreQ()
             throws Exception {
         setupLocationPermissions(Build.VERSION_CODES.P, true, AppOpsManager.OPSTR_COARSE_LOCATION,
                 Manifest.permission.ACCESS_COARSE_LOCATION);
 
-        final int myUid = Process.myUid();
-        assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        verifyOwnerUidAndTransportInfoNetCapsPermission(
+                // Ensure that we owner UID if the request asks us to remove it even if the app
+                // has necessary permissions since targetSdk >= S.
+                true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
+                // Ensure that we remove location info if the request asks to remove it even if the
+                // app has necessary permissions.
+                true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
+        );
     }
 
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedLocationOff() throws Exception {
+    public void testCreateWithLocationInfoSanitizedLocationOff() throws Exception {
         // Test that even with fine location permission, and UIDs matching, the UID is sanitized.
         setupLocationPermissions(Build.VERSION_CODES.Q, false, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
-        final int myUid = Process.myUid();
-        assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        verifyOwnerUidAndTransportInfoNetCapsPermission(
+                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
+                false /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
+        );
     }
 
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedWrongUid() throws Exception {
+    public void testCreateWithLocationInfoSanitizedWrongUid() throws Exception {
         // Test that even with fine location permission, not being the owner leads to sanitization.
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
         final int myUid = Process.myUid();
-        assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid + 1, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        assertEquals(Process.INVALID_UID,
+                getOwnerUidNetCapsPermission(myUid + 1, myUid,
+                        true /* includeLocationSensitiveInfo */));
     }
 
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedWithCoarseLocationAfterQ()
+    public void testCreateWithLocationInfoSanitizedWithCoarseLocationAfterQ()
             throws Exception {
         // Test that not having fine location permission leads to sanitization.
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_COARSE_LOCATION,
                 Manifest.permission.ACCESS_COARSE_LOCATION);
 
-        // Test that without the location permission, the owner field is sanitized.
-        final int myUid = Process.myUid();
-        assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        verifyOwnerUidAndTransportInfoNetCapsPermission(
+                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
+                false /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
+        );
     }
 
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedWithoutLocationPermission()
+    public void testCreateWithLocationInfoSanitizedWithoutLocationPermission()
             throws Exception {
+        // Test that not having fine location permission leads to sanitization.
         setupLocationPermissions(Build.VERSION_CODES.Q, true, null /* op */, null /* perm */);
 
-        // Test that without the location permission, the owner field is sanitized.
-        final int myUid = Process.myUid();
-        assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+        verifyOwnerUidAndTransportInfoNetCapsPermission(
+                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
+                false /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
+        );
+    }
 
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+    @Test
+    public void testCreateForCallerWithLocalMacAddressSanitizedWithLocalMacAddressPermission()
+            throws Exception {
+        mServiceContext.setPermission(Manifest.permission.LOCAL_MAC_ADDRESS, PERMISSION_GRANTED);
+
+        final TransportInfo transportInfo = mock(TransportInfo.class);
+        when(transportInfo.getApplicableRedactions())
+                .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS);
+        final NetworkCapabilities netCap =
+                new NetworkCapabilities().setTransportInfo(transportInfo);
+
+        mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
+                netCap, false /* includeLocationSensitiveInfoInTransportInfo */,
+                Process.myPid(), Process.myUid(),
+                mContext.getPackageName(), getAttributionTag());
+        // don't redact MAC_ADDRESS fields, only location sensitive fields.
+        verify(transportInfo).makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION);
+    }
+
+    @Test
+    public void testCreateForCallerWithLocalMacAddressSanitizedWithoutLocalMacAddressPermission()
+            throws Exception {
+        mServiceContext.setPermission(Manifest.permission.LOCAL_MAC_ADDRESS, PERMISSION_DENIED);
+
+        final TransportInfo transportInfo = mock(TransportInfo.class);
+        when(transportInfo.getApplicableRedactions())
+                .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS);
+        final NetworkCapabilities netCap =
+                new NetworkCapabilities().setTransportInfo(transportInfo);
+
+        mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
+                netCap, false /* includeLocationSensitiveInfoInTransportInfo */,
+                Process.myPid(), Process.myUid(),
+                mContext.getPackageName(), getAttributionTag());
+        // redact both MAC_ADDRESS & location sensitive fields.
+        verify(transportInfo).makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION
+                | REDACT_FOR_LOCAL_MAC_ADDRESS);
+    }
+
+    @Test
+    public void testCreateForCallerWithLocalMacAddressSanitizedWithSettingsPermission()
+            throws Exception {
+        mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
+
+        final TransportInfo transportInfo = mock(TransportInfo.class);
+        when(transportInfo.getApplicableRedactions())
+                .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_NETWORK_SETTINGS);
+        final NetworkCapabilities netCap =
+                new NetworkCapabilities().setTransportInfo(transportInfo);
+
+        mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
+                netCap, false /* includeLocationSensitiveInfoInTransportInfo */,
+                Process.myPid(), Process.myUid(),
+                mContext.getPackageName(), getAttributionTag());
+        // don't redact NETWORK_SETTINGS fields, only location sensitive fields.
+        verify(transportInfo).makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION);
+    }
+
+    @Test
+    public void testCreateForCallerWithLocalMacAddressSanitizedWithoutSettingsPermission()
+            throws Exception {
+        mServiceContext.setPermission(Manifest.permission.LOCAL_MAC_ADDRESS, PERMISSION_DENIED);
+
+        final TransportInfo transportInfo = mock(TransportInfo.class);
+        when(transportInfo.getApplicableRedactions())
+                .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_NETWORK_SETTINGS);
+        final NetworkCapabilities netCap =
+                new NetworkCapabilities().setTransportInfo(transportInfo);
+
+        mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
+                netCap, false /* includeLocationSensitiveInfoInTransportInfo */,
+                Process.myPid(), Process.myUid(),
+                mContext.getPackageName(), getAttributionTag());
+        // redact both NETWORK_SETTINGS & location sensitive fields.
+        verify(transportInfo).makeCopy(
+                REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_NETWORK_SETTINGS);
+    }
+
+    /**
+     * Test TransportInfo to verify redaction mechanism.
+     */
+    private static class TestTransportInfo implements TransportInfo {
+        public final boolean locationRedacted;
+        public final boolean localMacAddressRedacted;
+        public final boolean settingsRedacted;
+
+        TestTransportInfo() {
+            locationRedacted = false;
+            localMacAddressRedacted = false;
+            settingsRedacted = false;
+        }
+
+        TestTransportInfo(boolean locationRedacted, boolean localMacAddressRedacted,
+                boolean settingsRedacted) {
+            this.locationRedacted = locationRedacted;
+            this.localMacAddressRedacted =
+                    localMacAddressRedacted;
+            this.settingsRedacted = settingsRedacted;
+        }
+
+        @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
+            );
+        }
+
+        @Override
+        public @NetworkCapabilities.RedactionType long getApplicableRedactions() {
+            return REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS
+                    | REDACT_FOR_NETWORK_SETTINGS;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (!(other instanceof TestTransportInfo)) return false;
+            TestTransportInfo that = (TestTransportInfo) other;
+            return that.locationRedacted == this.locationRedacted
+                    && that.localMacAddressRedacted == this.localMacAddressRedacted
+                    && that.settingsRedacted == this.settingsRedacted;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(locationRedacted, localMacAddressRedacted, settingsRedacted);
+        }
+    }
+
+    private void verifyNetworkCallbackLocationDataInclusionUsingTransportInfoAndOwnerUidInNetCaps(
+            @NonNull TestNetworkCallback wifiNetworkCallback, int actualOwnerUid,
+            @NonNull TransportInfo actualTransportInfo, int expectedOwnerUid,
+            @NonNull TransportInfo expectedTransportInfo) throws Exception {
+        when(mPackageManager.getTargetSdkVersion(anyString())).thenReturn(Build.VERSION_CODES.S);
+        final NetworkCapabilities ncTemplate =
+                new NetworkCapabilities()
+                        .addTransportType(TRANSPORT_WIFI)
+                        .setOwnerUid(actualOwnerUid);
+
+        final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_WIFI).build();
+        mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
+
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, new LinkProperties(),
+                ncTemplate);
+        mWiFiNetworkAgent.connect(false);
+
+        wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+        // Send network capabilities update with TransportInfo to trigger capabilities changed
+        // callback.
+        mWiFiNetworkAgent.setNetworkCapabilities(
+                ncTemplate.setTransportInfo(actualTransportInfo), true);
+
+        wifiNetworkCallback.expectCapabilitiesThat(mWiFiNetworkAgent,
+                nc -> Objects.equals(expectedOwnerUid, nc.getOwnerUid())
+                        && Objects.equals(expectedTransportInfo, nc.getTransportInfo()));
+
+    }
+
+    @Test
+    public void testVerifyLocationDataIsNotIncludedWhenInclFlagNotSet() throws Exception {
+        final TestNetworkCallback wifiNetworkCallack = new TestNetworkCallback();
+        final int ownerUid = Process.myUid();
+        final TransportInfo transportInfo = new TestTransportInfo();
+        // Even though the test uid holds privileged permissions, mask location fields since
+        // the callback did not explicitly opt-in to get location data.
+        final TransportInfo sanitizedTransportInfo = new TestTransportInfo(
+                true, /* locationRedacted */
+                true, /* localMacAddressRedacted */
+                true /* settingsRedacted */
+        );
+        // Should not expect location data since the callback does not set the flag for including
+        // location data.
+        verifyNetworkCallbackLocationDataInclusionUsingTransportInfoAndOwnerUidInNetCaps(
+                wifiNetworkCallack, ownerUid, transportInfo, INVALID_UID, sanitizedTransportInfo);
     }
 
     private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
             throws Exception {
-        final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER));
+        final Set<UidRange> vpnRange = Collections.singleton(PRIMARY_UIDRANGE);
         mMockVpn.setVpnType(vpnType);
         mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange);
         assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid);
@@ -8972,8 +9493,9 @@
                 ConnectivityManager.getNetworkTypeName(TYPE_MOBILE),
                 TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_LTE));
         return new NetworkAgentInfo(null, new Network(NET_ID), info, new LinkProperties(),
-                nc, 0, mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0,
-                INVALID_UID, mQosCallbackTracker);
+                nc, new NetworkScore.Builder().setLegacyInt(0).build(),
+                mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0,
+                INVALID_UID, mQosCallbackTracker, new ConnectivityService.Dependencies());
     }
 
     @Test
@@ -9314,10 +9836,12 @@
         assertContainsExactly(exemptUidCaptor.getValue(), Process.VPN_UID, exemptUid);
 
         if (add) {
-            inOrder.verify(mMockNetd, times(1)).networkAddUidRanges(eq(mMockVpn.getNetId()),
+            inOrder.verify(mMockNetd, times(1))
+                    .networkAddUidRanges(eq(mMockVpn.getNetwork().getNetId()),
                     eq(toUidRangeStableParcels(vpnRanges)));
         } else {
-            inOrder.verify(mMockNetd, times(1)).networkRemoveUidRanges(eq(mMockVpn.getNetId()),
+            inOrder.verify(mMockNetd, times(1))
+                    .networkRemoveUidRanges(eq(mMockVpn.getNetwork().getNetId()),
                     eq(toUidRangeStableParcels(vpnRanges)));
         }
 
@@ -9332,7 +9856,7 @@
         lp.setInterfaceName("tun0");
         lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
-        final UidRange vpnRange = createUidRange(PRIMARY_USER);
+        final UidRange vpnRange = PRIMARY_UIDRANGE;
         Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
         mMockVpn.establish(lp, VPN_UID, vpnRanges);
         assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
@@ -9358,9 +9882,9 @@
         for (int reqTypeInt : invalidReqTypeInts) {
             assertThrows("Expect throws for invalid request type " + reqTypeInt,
                     IllegalArgumentException.class,
-                    () -> mService.requestNetwork(nc, reqTypeInt, null, 0, null,
-                            ConnectivityManager.TYPE_NONE, mContext.getPackageName(),
-                            getAttributionTag())
+                    () -> mService.requestNetwork(Process.INVALID_UID, nc, reqTypeInt, null, 0,
+                            null, ConnectivityManager.TYPE_NONE, NetworkCallback.FLAG_NONE,
+                            mContext.getPackageName(), getAttributionTag())
             );
         }
     }
@@ -9478,7 +10002,7 @@
                         && session.getSessionType() == QosSession.TYPE_EPS_BEARER), eq(attributes));
 
         mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent()
-                .sendQosSessionLost(qosCallbackId, sessionId);
+                .sendQosSessionLost(qosCallbackId, sessionId, QosSession.TYPE_EPS_BEARER);
         waitForIdle();
         verify(mQosCallbackMockHelper.mCallback).onQosSessionLost(argThat(session ->
                 session.getSessionId() == sessionId
@@ -9486,6 +10010,36 @@
     }
 
     @Test
+    public void testNrQosCallbackAvailableAndLost() throws Exception {
+        mQosCallbackMockHelper = new QosCallbackMockHelper();
+        final int sessionId = 10;
+        final int qosCallbackId = 1;
+
+        when(mQosCallbackMockHelper.mFilter.validate())
+                .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
+        mQosCallbackMockHelper.registerQosCallback(
+                mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
+        waitForIdle();
+
+        final NrQosSessionAttributes attributes = new NrQosSessionAttributes(
+                1, 2, 3, 4, 5, 6, 7, new ArrayList<>());
+        mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent()
+                .sendQosSessionAvailable(qosCallbackId, sessionId, attributes);
+        waitForIdle();
+
+        verify(mQosCallbackMockHelper.mCallback).onNrQosSessionAvailable(argThat(session ->
+                session.getSessionId() == sessionId
+                        && session.getSessionType() == QosSession.TYPE_NR_BEARER), eq(attributes));
+
+        mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent()
+                .sendQosSessionLost(qosCallbackId, sessionId, QosSession.TYPE_NR_BEARER);
+        waitForIdle();
+        verify(mQosCallbackMockHelper.mCallback).onQosSessionLost(argThat(session ->
+                session.getSessionId() == sessionId
+                        && session.getSessionType() == QosSession.TYPE_NR_BEARER));
+    }
+
+    @Test
     public void testQosCallbackTooManyRequests() throws Exception {
         mQosCallbackMockHelper = new QosCallbackMockHelper();
 
@@ -9530,7 +10084,7 @@
                 .thenReturn(hasFeature);
     }
 
-    private UidRange getNriFirstUidRange(
+    private Range<Integer> getNriFirstUidRange(
             @NonNull final ConnectivityService.NetworkRequestInfo nri) {
         return nri.mRequests.get(0).networkCapabilities.getUids().iterator().next();
     }
@@ -9713,11 +10267,11 @@
                                 pref));
 
         // Sort by uid to access nris by index
-        nris.sort(Comparator.comparingInt(nri -> getNriFirstUidRange(nri).start));
-        assertEquals(TEST_PACKAGE_UID, getNriFirstUidRange(nris.get(0)).start);
-        assertEquals(TEST_PACKAGE_UID, getNriFirstUidRange(nris.get(0)).stop);
-        assertEquals(testPackageNameUid2, getNriFirstUidRange(nris.get(1)).start);
-        assertEquals(testPackageNameUid2, getNriFirstUidRange(nris.get(1)).stop);
+        nris.sort(Comparator.comparingInt(nri -> getNriFirstUidRange(nri).getLower()));
+        assertEquals(TEST_PACKAGE_UID, (int) getNriFirstUidRange(nris.get(0)).getLower());
+        assertEquals(TEST_PACKAGE_UID, (int) getNriFirstUidRange(nris.get(0)).getUpper());
+        assertEquals(testPackageNameUid2, (int) getNriFirstUidRange(nris.get(1)).getLower());
+        assertEquals(testPackageNameUid2, (int) getNriFirstUidRange(nris.get(1)).getUpper());
     }
 
     @Test
@@ -9747,17 +10301,17 @@
         // UIDs for all users and all managed packages should be present.
         // Two users each with two packages.
         final int expectedUidSize = 2;
-        final List<UidRange> uids =
+        final List<Range<Integer>> uids =
                 new ArrayList<>(nris.get(0).mRequests.get(0).networkCapabilities.getUids());
         assertEquals(expectedUidSize, uids.size());
 
         // Sort by uid to access nris by index
-        uids.sort(Comparator.comparingInt(uid -> uid.start));
+        uids.sort(Comparator.comparingInt(uid -> uid.getLower()));
         final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID);
-        assertEquals(TEST_PACKAGE_UID, uids.get(0).start);
-        assertEquals(TEST_PACKAGE_UID, uids.get(0).stop);
-        assertEquals(secondUserTestPackageUid, uids.get(1).start);
-        assertEquals(secondUserTestPackageUid, uids.get(1).stop);
+        assertEquals(TEST_PACKAGE_UID, (int) uids.get(0).getLower());
+        assertEquals(TEST_PACKAGE_UID, (int) uids.get(0).getUpper());
+        assertEquals(secondUserTestPackageUid, (int) uids.get(1).getLower());
+        assertEquals(secondUserTestPackageUid, (int) uids.get(1).getUpper());
     }
 
     @Test
@@ -9924,9 +10478,13 @@
                 Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
         mSystemDefaultNetworkCallback = new TestNetworkCallback();
         mDefaultNetworkCallback = new TestNetworkCallback();
+        mProfileDefaultNetworkCallback = new TestNetworkCallback();
         mCm.registerSystemDefaultNetworkCallback(mSystemDefaultNetworkCallback,
                 new Handler(ConnectivityThread.getInstanceLooper()));
         mCm.registerDefaultNetworkCallback(mDefaultNetworkCallback);
+        registerDefaultNetworkCallbackAsUid(mProfileDefaultNetworkCallback,
+                TEST_WORK_PROFILE_APP_UID);
+        // TODO: test using ConnectivityManager#registerDefaultNetworkCallbackAsUid as well.
         mServiceContext.setPermission(
                 Manifest.permission.NETWORK_SETTINGS, PERMISSION_DENIED);
     }
@@ -9938,12 +10496,15 @@
         if (null != mSystemDefaultNetworkCallback) {
             mCm.unregisterNetworkCallback(mSystemDefaultNetworkCallback);
         }
+        if (null != mProfileDefaultNetworkCallback) {
+            mCm.unregisterNetworkCallback(mProfileDefaultNetworkCallback);
+        }
     }
 
     private void setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(
             @OemNetworkPreferences.OemNetworkPreference final int networkPrefToSetup)
             throws Exception {
-        final int testPackageNameUid = 123;
+        final int testPackageNameUid = TEST_PACKAGE_UID;
         final String testPackageName = "per.app.defaults.package";
         setupMultipleDefaultNetworksForOemNetworkPreferenceTest(
                 networkPrefToSetup, testPackageNameUid, testPackageName);
@@ -9993,7 +10554,7 @@
         oemPrefListener.expectOnComplete();
     }
 
-    private static class TestOemListenerCallback implements IOnSetOemNetworkPreferenceListener {
+    private static class TestOemListenerCallback implements IOnCompleteListener {
         final CompletableFuture<Object> mDone = new CompletableFuture<>();
 
         @Override
@@ -10079,6 +10640,11 @@
         mCm.registerDefaultNetworkCallback(defaultNetworkCallback);
         defaultNetworkCallback.assertNoCallback();
 
+        final TestNetworkCallback otherUidDefaultCallback = new TestNetworkCallback();
+        withPermission(Manifest.permission.NETWORK_SETTINGS, () ->
+                mCm.registerDefaultNetworkCallbackAsUid(TEST_PACKAGE_UID, otherUidDefaultCallback,
+                        new Handler(ConnectivityThread.getInstanceLooper())));
+
         // Setup the test process to use networkPref for their default network.
         setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref);
 
@@ -10089,19 +10655,22 @@
                 null,
                 mEthernetNetworkAgent.getNetwork());
 
-        // At this point with a restricted network used, the available callback should trigger
+        // At this point with a restricted network used, the available callback should trigger.
         defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
         assertEquals(defaultNetworkCallback.getLastAvailableNetwork(),
                 mEthernetNetworkAgent.getNetwork());
+        otherUidDefaultCallback.assertNoCallback();
 
         // Now bring down the default network which should trigger a LOST callback.
         setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false);
 
         // At this point, with no network is available, the lost callback should trigger
         defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
+        otherUidDefaultCallback.assertNoCallback();
 
         // Confirm we can unregister without issues.
         mCm.unregisterNetworkCallback(defaultNetworkCallback);
+        mCm.unregisterNetworkCallback(otherUidDefaultCallback);
     }
 
     @Test
@@ -10119,6 +10688,11 @@
         mCm.registerDefaultNetworkCallback(defaultNetworkCallback);
         defaultNetworkCallback.assertNoCallback();
 
+        final TestNetworkCallback otherUidDefaultCallback = new TestNetworkCallback();
+        withPermission(Manifest.permission.NETWORK_SETTINGS, () ->
+                mCm.registerDefaultNetworkCallbackAsUid(TEST_PACKAGE_UID, otherUidDefaultCallback,
+                        new Handler(ConnectivityThread.getInstanceLooper())));
+
         // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID.
         // The active nai for the default is null at this point as this is a restricted network.
         setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true);
@@ -10130,15 +10704,19 @@
         defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
         assertEquals(defaultNetworkCallback.getLastAvailableNetwork(),
                 mEthernetNetworkAgent.getNetwork());
+        otherUidDefaultCallback.assertNoCallback();
 
         // Now bring down the default network which should trigger a LOST callback.
         setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false);
+        otherUidDefaultCallback.assertNoCallback();
 
         // At this point, with no network is available, the lost callback should trigger
         defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
+        otherUidDefaultCallback.assertNoCallback();
 
         // Confirm we can unregister without issues.
         mCm.unregisterNetworkCallback(defaultNetworkCallback);
+        mCm.unregisterNetworkCallback(otherUidDefaultCallback);
     }
 
     @Test
@@ -10152,6 +10730,11 @@
         mCm.registerDefaultNetworkCallback(defaultNetworkCallback);
         defaultNetworkCallback.assertNoCallback();
 
+        final TestNetworkCallback otherUidDefaultCallback = new TestNetworkCallback();
+        withPermission(Manifest.permission.NETWORK_SETTINGS, () ->
+                mCm.registerDefaultNetworkCallbackAsUid(TEST_PACKAGE_UID, otherUidDefaultCallback,
+                        new Handler(ConnectivityThread.getInstanceLooper())));
+
         // Setup a process different than the test process to use the default network. This means
         // that the defaultNetworkCallback won't be tracked by the per-app policy.
         setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(networkPref);
@@ -10167,6 +10750,9 @@
         defaultNetworkCallback.assertNoCallback();
         assertDefaultNetworkCapabilities(userId /* no networks */);
 
+        // The other UID does have access, and gets a callback.
+        otherUidDefaultCallback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
+
         // Bring up unrestricted cellular. This should now satisfy the default network.
         setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
         verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
@@ -10174,25 +10760,31 @@
                 mEthernetNetworkAgent.getNetwork());
 
         // At this point with an unrestricted network used, the available callback should trigger
+        // The other UID is unaffected and remains on the paid network.
         defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         assertEquals(defaultNetworkCallback.getLastAvailableNetwork(),
                 mCellNetworkAgent.getNetwork());
         assertDefaultNetworkCapabilities(userId, mCellNetworkAgent);
+        otherUidDefaultCallback.assertNoCallback();
 
         // Now bring down the per-app network.
         setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false);
 
-        // Since the callback didn't use the per-app network, no callback should fire.
+        // Since the callback didn't use the per-app network, only the other UID gets a callback.
+        // Because the preference specifies no fallback, it does not switch to cellular.
         defaultNetworkCallback.assertNoCallback();
+        otherUidDefaultCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
 
         // Now bring down the default network.
         setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false);
 
         // As this callback was tracking the default, this should now trigger.
         defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        otherUidDefaultCallback.assertNoCallback();
 
         // Confirm we can unregister without issues.
         mCm.unregisterNetworkCallback(defaultNetworkCallback);
+        mCm.unregisterNetworkCallback(otherUidDefaultCallback);
     }
 
     /**
@@ -10930,4 +11522,495 @@
         verifyNoNetwork();
         mCm.unregisterNetworkCallback(cellCb);
     }
+
+    // Cannot be part of MockNetworkFactory since it requires method of the test.
+    private void expectNoRequestChanged(@NonNull MockNetworkFactory factory) {
+        waitForIdle();
+        factory.assertNoRequestChanged();
+    }
+
+    @Test
+    public void testRegisterBestMatchingNetworkCallback_noIssueToFactory() throws Exception {
+        // Prepare mock mms factory.
+        final HandlerThread handlerThread = new HandlerThread("MockCellularFactory");
+        handlerThread.start();
+        NetworkCapabilities filter = new NetworkCapabilities()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NET_CAPABILITY_MMS);
+        final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
+                mServiceContext, "testFactory", filter, mCsHandlerThread);
+        testFactory.setScoreFilter(40);
+
+        try {
+            // Register the factory and expect it will see default request, because all requests
+            // are sent to all factories.
+            testFactory.register();
+            testFactory.expectRequestAdd();
+            testFactory.assertRequestCountEquals(1);
+            // The factory won't try to start the network since the default request doesn't
+            // match the filter (no INTERNET capability).
+            assertFalse(testFactory.getMyStartRequested());
+
+            // Register callback for listening best matching network. Verify that the request won't
+            // be sent to factory.
+            final TestNetworkCallback bestMatchingCb = new TestNetworkCallback();
+            mCm.registerBestMatchingNetworkCallback(
+                    new NetworkRequest.Builder().addCapability(NET_CAPABILITY_MMS).build(),
+                    bestMatchingCb, mCsHandlerThread.getThreadHandler());
+            bestMatchingCb.assertNoCallback();
+            expectNoRequestChanged(testFactory);
+            testFactory.assertRequestCountEquals(1);
+            assertFalse(testFactory.getMyStartRequested());
+
+            // Fire a normal mms request, verify the factory will only see the request.
+            final TestNetworkCallback mmsNetworkCallback = new TestNetworkCallback();
+            final NetworkRequest mmsRequest = new NetworkRequest.Builder()
+                    .addCapability(NET_CAPABILITY_MMS).build();
+            mCm.requestNetwork(mmsRequest, mmsNetworkCallback);
+            testFactory.expectRequestAdd();
+            testFactory.assertRequestCountEquals(2);
+            assertTrue(testFactory.getMyStartRequested());
+
+            // Unregister best matching callback, verify factory see no change.
+            mCm.unregisterNetworkCallback(bestMatchingCb);
+            expectNoRequestChanged(testFactory);
+            testFactory.assertRequestCountEquals(2);
+            assertTrue(testFactory.getMyStartRequested());
+        } finally {
+            testFactory.terminate();
+        }
+    }
+
+    @Test
+    public void testRegisterBestMatchingNetworkCallback_trackBestNetwork() throws Exception {
+        final TestNetworkCallback bestMatchingCb = new TestNetworkCallback();
+        mCm.registerBestMatchingNetworkCallback(
+                new NetworkRequest.Builder().addCapability(NET_CAPABILITY_TRUSTED).build(),
+                bestMatchingCb, mCsHandlerThread.getThreadHandler());
+
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        bestMatchingCb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(true);
+        bestMatchingCb.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+
+        // Change something on cellular to trigger capabilities changed, since the callback
+        // only cares about the best network, verify it received nothing from cellular.
+        mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+        bestMatchingCb.assertNoCallback();
+
+        // Make cellular the best network again, verify the callback now tracks cellular.
+        mWiFiNetworkAgent.adjustScore(-50);
+        bestMatchingCb.expectAvailableCallbacksValidated(mCellNetworkAgent);
+
+        // Make cellular temporary non-trusted, which will not satisfying the request.
+        // Verify the callback switch from/to the other network accordingly.
+        mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
+        bestMatchingCb.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
+        mCellNetworkAgent.addCapability(NET_CAPABILITY_TRUSTED);
+        bestMatchingCb.expectAvailableDoubleValidatedCallbacks(mCellNetworkAgent);
+
+        // Verify the callback doesn't care about wifi disconnect.
+        mWiFiNetworkAgent.disconnect();
+        bestMatchingCb.assertNoCallback();
+        mCellNetworkAgent.disconnect();
+        bestMatchingCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+    }
+
+    private UidRangeParcel[] uidRangeFor(final UserHandle handle) {
+        UidRange range = UidRange.createForUser(handle);
+        return new UidRangeParcel[] { new UidRangeParcel(range.start, range.stop) };
+    }
+
+    private static class TestOnCompleteListener implements Runnable {
+        final class OnComplete {}
+        final ArrayTrackRecord<OnComplete>.ReadHead mHistory =
+                new ArrayTrackRecord<OnComplete>().newReadHead();
+
+        @Override
+        public void run() {
+            mHistory.add(new OnComplete());
+        }
+
+        public void expectOnComplete() {
+            assertNotNull(mHistory.poll(TIMEOUT_MS, it -> true));
+        }
+    }
+
+    private TestNetworkAgentWrapper makeEnterpriseNetworkAgent() throws Exception {
+        final NetworkCapabilities workNc = new NetworkCapabilities();
+        workNc.addCapability(NET_CAPABILITY_ENTERPRISE);
+        workNc.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
+        return new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, new LinkProperties(), workNc);
+    }
+
+    private TestNetworkCallback mEnterpriseCallback;
+    private UserHandle setupEnterpriseNetwork() {
+        final UserHandle userHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
+        mServiceContext.setWorkProfile(userHandle, true);
+
+        // File a request to avoid the enterprise network being disconnected as soon as the default
+        // request goes away – it would make impossible to test that networkRemoveUidRanges
+        // is called, as the network would disconnect first for lack of a request.
+        mEnterpriseCallback = new TestNetworkCallback();
+        final NetworkRequest keepUpRequest = new NetworkRequest.Builder()
+                .addCapability(NET_CAPABILITY_ENTERPRISE)
+                .build();
+        mCm.requestNetwork(keepUpRequest, mEnterpriseCallback);
+        return userHandle;
+    }
+
+    private void maybeTearDownEnterpriseNetwork() {
+        if (null != mEnterpriseCallback) {
+            mCm.unregisterNetworkCallback(mEnterpriseCallback);
+        }
+    }
+
+    /**
+     * Make sure per-profile networking preference behaves as expected when the enterprise network
+     * goes up and down while the preference is active. Make sure they behave as expected whether
+     * there is a general default network or not.
+     */
+    @Test
+    public void testPreferenceForUserNetworkUpDown() throws Exception {
+        final InOrder inOrder = inOrder(mMockNetd);
+        final UserHandle testHandle = setupEnterpriseNetwork();
+        registerDefaultNetworkCallbacks();
+
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+
+        mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        mProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
+                INetd.PERMISSION_NONE);
+
+        final TestOnCompleteListener listener = new TestOnCompleteListener();
+        mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+                r -> r.run(), listener);
+        listener.expectOnComplete();
+
+        // Setting a network preference for this user will create a new set of routing rules for
+        // the UID range that corresponds to this user, so as to define the default network
+        // for these apps separately. This is true because the multi-layer request relevant to
+        // this UID range contains a TRACK_DEFAULT, so the range will be moved through UID-specific
+        // rules to the correct network – in this case the system default network. The case where
+        // the default network for the profile happens to be the same as the system default
+        // is not handled specially, the rules are always active as long as a preference is set.
+        inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId,
+                uidRangeFor(testHandle));
+
+        // The enterprise network is not ready yet.
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
+                mProfileDefaultNetworkCallback);
+
+        final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent();
+        workAgent.connect(false);
+
+        mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent);
+        mSystemDefaultNetworkCallback.assertNoCallback();
+        mDefaultNetworkCallback.assertNoCallback();
+        inOrder.verify(mMockNetd).networkCreatePhysical(workAgent.getNetwork().netId,
+                INetd.PERMISSION_SYSTEM);
+        inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
+                uidRangeFor(testHandle));
+        inOrder.verify(mMockNetd).networkRemoveUidRanges(mCellNetworkAgent.getNetwork().netId,
+                uidRangeFor(testHandle));
+
+        // Make sure changes to the work agent send callbacks to the app in the work profile, but
+        // not to the other apps.
+        workAgent.setNetworkValid(true /* isStrictMode */);
+        workAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+        mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent,
+                nc -> nc.hasCapability(NET_CAPABILITY_VALIDATED)
+                        && nc.hasCapability(NET_CAPABILITY_ENTERPRISE));
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+
+        workAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+        mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent, nc ->
+                nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+
+        // Conversely, change a capability on the system-wide default network and make sure
+        // that only the apps outside of the work profile receive the callbacks.
+        mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+        mSystemDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc ->
+                nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+        mDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc ->
+                nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+        mProfileDefaultNetworkCallback.assertNoCallback();
+
+        // Disconnect and reconnect the system-wide default network and make sure that the
+        // apps on this network see the appropriate callbacks, and the app on the work profile
+        // doesn't because it continues to use the enterprise network.
+        mCellNetworkAgent.disconnect();
+        mSystemDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        mProfileDefaultNetworkCallback.assertNoCallback();
+        inOrder.verify(mMockNetd).networkDestroy(mCellNetworkAgent.getNetwork().netId);
+
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        mProfileDefaultNetworkCallback.assertNoCallback();
+        inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
+                INetd.PERMISSION_NONE);
+
+        // When the agent disconnects, test that the app on the work profile falls back to the
+        // default network.
+        workAgent.disconnect();
+        mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent);
+        mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+        inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId,
+                uidRangeFor(testHandle));
+        inOrder.verify(mMockNetd).networkDestroy(workAgent.getNetwork().netId);
+
+        mCellNetworkAgent.disconnect();
+        mSystemDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+
+        // Waiting for the handler to be idle before checking for networkDestroy is necessary
+        // here because ConnectivityService calls onLost before the network is fully torn down.
+        waitForIdle();
+        inOrder.verify(mMockNetd).networkDestroy(mCellNetworkAgent.getNetwork().netId);
+
+        // If the control comes here, callbacks seem to behave correctly in the presence of
+        // a default network when the enterprise network goes up and down. Now, make sure they
+        // also behave correctly in the absence of a system-wide default network.
+        final TestNetworkAgentWrapper workAgent2 = makeEnterpriseNetworkAgent();
+        workAgent2.connect(false);
+
+        mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent2);
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+        inOrder.verify(mMockNetd).networkCreatePhysical(workAgent2.getNetwork().netId,
+                INetd.PERMISSION_SYSTEM);
+        inOrder.verify(mMockNetd).networkAddUidRanges(workAgent2.getNetwork().netId,
+                uidRangeFor(testHandle));
+
+        workAgent2.setNetworkValid(true /* isStrictMode */);
+        workAgent2.mNetworkMonitor.forceReevaluation(Process.myUid());
+        mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent2,
+                nc -> nc.hasCapability(NET_CAPABILITY_ENTERPRISE)
+                        && !nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+        inOrder.verify(mMockNetd, never()).networkAddUidRanges(anyInt(), any());
+
+        // When the agent disconnects, test that the app on the work profile falls back to the
+        // default network.
+        workAgent2.disconnect();
+        mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent2);
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+        inOrder.verify(mMockNetd).networkDestroy(workAgent2.getNetwork().netId);
+
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
+                mProfileDefaultNetworkCallback);
+
+        // Callbacks will be unregistered by tearDown()
+    }
+
+    /**
+     * Test that, in a given networking context, calling setPreferenceForUser to set per-profile
+     * defaults on then off works as expected.
+     */
+    @Test
+    public void testSetPreferenceForUserOnOff() throws Exception {
+        final InOrder inOrder = inOrder(mMockNetd);
+        final UserHandle testHandle = setupEnterpriseNetwork();
+
+        // Connect both a regular cell agent and an enterprise network first.
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+
+        final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent();
+        workAgent.connect(true);
+
+        final TestOnCompleteListener listener = new TestOnCompleteListener();
+        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).networkAddUidRanges(workAgent.getNetwork().netId,
+                uidRangeFor(testHandle));
+
+        registerDefaultNetworkCallbacks();
+
+        mSystemDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+        mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+        mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(workAgent);
+
+        mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_DEFAULT,
+                r -> r.run(), listener);
+        listener.expectOnComplete();
+
+        mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+        inOrder.verify(mMockNetd).networkRemoveUidRanges(workAgent.getNetwork().netId,
+                uidRangeFor(testHandle));
+
+        workAgent.disconnect();
+        mCellNetworkAgent.disconnect();
+
+        // Callbacks will be unregistered by tearDown()
+    }
+
+    /**
+     * Test per-profile default networks for two different profiles concurrently.
+     */
+    @Test
+    public void testSetPreferenceForTwoProfiles() throws Exception {
+        final InOrder inOrder = inOrder(mMockNetd);
+        final UserHandle testHandle2 = setupEnterpriseNetwork();
+        final UserHandle testHandle4 = UserHandle.of(TEST_WORK_PROFILE_USER_ID + 2);
+        mServiceContext.setWorkProfile(testHandle4, true);
+        registerDefaultNetworkCallbacks();
+
+        final TestNetworkCallback app4Cb = new TestNetworkCallback();
+        final int testWorkProfileAppUid4 =
+                UserHandle.getUid(testHandle4.getIdentifier(), TEST_APP_ID);
+        registerDefaultNetworkCallbackAsUid(app4Cb, testWorkProfileAppUid4);
+
+        // Connect both a regular cell agent and an enterprise network first.
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+
+        final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent();
+        workAgent.connect(true);
+
+        mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        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);
+
+        final TestOnCompleteListener listener = new TestOnCompleteListener();
+        mCm.setProfileNetworkPreference(testHandle2, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+                r -> r.run(), listener);
+        listener.expectOnComplete();
+        inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
+                uidRangeFor(testHandle2));
+
+        mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(workAgent);
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
+                app4Cb);
+
+        mCm.setProfileNetworkPreference(testHandle4, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+                r -> r.run(), listener);
+        listener.expectOnComplete();
+        inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
+                uidRangeFor(testHandle4));
+
+        app4Cb.expectAvailableCallbacksValidated(workAgent);
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
+                mProfileDefaultNetworkCallback);
+
+        mCm.setProfileNetworkPreference(testHandle2, PROFILE_NETWORK_PREFERENCE_DEFAULT,
+                r -> r.run(), listener);
+        listener.expectOnComplete();
+        inOrder.verify(mMockNetd).networkRemoveUidRanges(workAgent.getNetwork().netId,
+                uidRangeFor(testHandle2));
+
+        mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
+                app4Cb);
+
+        workAgent.disconnect();
+        mCellNetworkAgent.disconnect();
+
+        mCm.unregisterNetworkCallback(app4Cb);
+        // Other callbacks will be unregistered by tearDown()
+    }
+
+    @Test
+    public void testProfilePreferenceRemovedUponUserRemoved() throws Exception {
+        final InOrder inOrder = inOrder(mMockNetd);
+        final UserHandle testHandle = setupEnterpriseNetwork();
+
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+
+        final TestOnCompleteListener listener = new TestOnCompleteListener();
+        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).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId,
+                uidRangeFor(testHandle));
+
+        final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
+        removedIntent.putExtra(Intent.EXTRA_USER, testHandle);
+        processBroadcast(removedIntent);
+
+        inOrder.verify(mMockNetd).networkRemoveUidRanges(mCellNetworkAgent.getNetwork().netId,
+                uidRangeFor(testHandle));
+    }
+
+    /**
+     * Make sure that OEM preference and per-profile preference can't be used at the same
+     * time and throw ISE if tried
+     */
+    @Test
+    public void testOemPreferenceAndProfilePreferenceExclusive() throws Exception {
+        final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
+        mServiceContext.setWorkProfile(testHandle, true);
+        final TestOnCompleteListener listener = new TestOnCompleteListener();
+
+        setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(
+                OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY);
+        assertThrows("Should not be able to set per-profile pref while OEM prefs present",
+                IllegalStateException.class, () ->
+                        mCm.setProfileNetworkPreference(testHandle,
+                                PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+                                r -> r.run(), listener));
+
+        // Empty the OEM prefs
+        final TestOemListenerCallback oemPrefListener = new TestOemListenerCallback();
+        final OemNetworkPreferences emptyOemPref = new OemNetworkPreferences.Builder().build();
+        mService.setOemNetworkPreference(emptyOemPref, oemPrefListener);
+        oemPrefListener.expectOnComplete();
+
+        mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+                r -> r.run(), listener);
+        listener.expectOnComplete();
+        assertThrows("Should not be able to set OEM prefs while per-profile pref is on",
+                IllegalStateException.class , () ->
+                        mService.setOemNetworkPreference(emptyOemPref, oemPrefListener));
+    }
+
+    /**
+     * Make sure wrong preferences for per-profile default networking are rejected.
+     */
+    @Test
+    public void testProfileNetworkPrefWrongPreference() throws Exception {
+        final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
+        mServiceContext.setWorkProfile(testHandle, true);
+        assertThrows("Should not be able to set an illegal preference",
+                IllegalArgumentException.class,
+                () -> mCm.setProfileNetworkPreference(testHandle,
+                        PROFILE_NETWORK_PREFERENCE_ENTERPRISE + 1, null, null));
+    }
+
+    /**
+     * Make sure requests for per-profile default networking for a non-work profile are
+     * rejected
+     */
+    @Test
+    public void testProfileNetworkPrefWrongProfile() throws Exception {
+        final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
+        mServiceContext.setWorkProfile(testHandle, false);
+        assertThrows("Should not be able to set a user pref for a non-work profile",
+                IllegalArgumentException.class , () ->
+                        mCm.setProfileNetworkPreference(testHandle,
+                                PROFILE_NETWORK_PREFERENCE_ENTERPRISE, null, null));
+    }
 }
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index f97eabf..6232423 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -35,6 +35,7 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.net.ConnectivityManager;
 import android.net.INetd;
 import android.net.IpSecAlgorithm;
 import android.net.IpSecConfig;
@@ -47,6 +48,7 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.StructStat;
+import android.util.Range;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -647,9 +649,9 @@
 
     @Test
     public void testReserveNetId() {
-        int start = mIpSecService.TUN_INTF_NETID_START;
-        for (int i = 0; i < mIpSecService.TUN_INTF_NETID_RANGE; i++) {
-            assertEquals(start + i, mIpSecService.reserveNetId());
+        final Range<Integer> netIdRange = ConnectivityManager.getIpSecNetIdRange();
+        for (int netId = netIdRange.getLower(); netId <= netIdRange.getUpper(); netId++) {
+            assertEquals(netId, mIpSecService.reserveNetId());
         }
 
         // Check that resource exhaustion triggers an exception
@@ -661,7 +663,7 @@
 
         // Now release one and try again
         int releasedNetId =
-                mIpSecService.TUN_INTF_NETID_START + mIpSecService.TUN_INTF_NETID_RANGE / 2;
+                netIdRange.getLower() + (netIdRange.getUpper() - netIdRange.getLower()) / 2;
         mIpSecService.releaseNetId(releasedNetId);
         assertEquals(releasedNetId, mIpSecService.reserveNetId());
     }
diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
index a10a3c8..5ec1119 100644
--- a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
+++ b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
@@ -21,13 +21,29 @@
 
 package com.android.server
 
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.FEATURE_WIFI
+import android.content.pm.PackageManager.FEATURE_WIFI_DIRECT
 import android.net.ConnectivityManager.TYPE_ETHERNET
 import android.net.ConnectivityManager.TYPE_MOBILE
+import android.net.ConnectivityManager.TYPE_MOBILE_CBS
+import android.net.ConnectivityManager.TYPE_MOBILE_DUN
+import android.net.ConnectivityManager.TYPE_MOBILE_EMERGENCY
+import android.net.ConnectivityManager.TYPE_MOBILE_FOTA
+import android.net.ConnectivityManager.TYPE_MOBILE_HIPRI
+import android.net.ConnectivityManager.TYPE_MOBILE_IA
+import android.net.ConnectivityManager.TYPE_MOBILE_IMS
+import android.net.ConnectivityManager.TYPE_MOBILE_MMS
 import android.net.ConnectivityManager.TYPE_MOBILE_SUPL
+import android.net.ConnectivityManager.TYPE_VPN
 import android.net.ConnectivityManager.TYPE_WIFI
+import android.net.ConnectivityManager.TYPE_WIFI_P2P
 import android.net.ConnectivityManager.TYPE_WIMAX
+import android.net.EthernetManager
 import android.net.NetworkInfo.DetailedState.CONNECTED
 import android.net.NetworkInfo.DetailedState.DISCONNECTED
+import android.telephony.TelephonyManager
 import androidx.test.filters.SmallTest
 import androidx.test.runner.AndroidJUnit4
 import com.android.server.ConnectivityService.LegacyTypeTracker
@@ -36,7 +52,6 @@
 import org.junit.Assert.assertNull
 import org.junit.Assert.assertSame
 import org.junit.Assert.assertTrue
-import org.junit.Assert.fail
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.any
@@ -52,88 +67,130 @@
 @RunWith(AndroidJUnit4::class)
 @SmallTest
 class LegacyTypeTrackerTest {
-    private val supportedTypes = arrayOf(TYPE_MOBILE, TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_SUPL)
+    private val supportedTypes = arrayOf(TYPE_WIFI, TYPE_WIFI_P2P, TYPE_ETHERNET, TYPE_MOBILE,
+            TYPE_MOBILE_SUPL, TYPE_MOBILE_MMS, TYPE_MOBILE_SUPL, TYPE_MOBILE_DUN, TYPE_MOBILE_HIPRI,
+            TYPE_MOBILE_FOTA, TYPE_MOBILE_IMS, TYPE_MOBILE_CBS, TYPE_MOBILE_IA,
+            TYPE_MOBILE_EMERGENCY, TYPE_VPN)
 
     private val mMockService = mock(ConnectivityService::class.java).apply {
         doReturn(false).`when`(this).isDefaultNetwork(any())
     }
-    private val mTracker = LegacyTypeTracker(mMockService).apply {
-        supportedTypes.forEach {
-            addSupportedType(it)
-        }
+    private val mPm = mock(PackageManager::class.java)
+    private val mContext = mock(Context::class.java).apply {
+        doReturn(true).`when`(mPm).hasSystemFeature(FEATURE_WIFI)
+        doReturn(true).`when`(mPm).hasSystemFeature(FEATURE_WIFI_DIRECT)
+        doReturn(mPm).`when`(this).packageManager
+        doReturn(mock(EthernetManager::class.java)).`when`(this).getSystemService(
+                Context.ETHERNET_SERVICE)
+    }
+    private val mTm = mock(TelephonyManager::class.java).apply {
+        doReturn(true).`when`(this).isDataCapable
+    }
+
+    private fun makeTracker() = LegacyTypeTracker(mMockService).apply {
+        loadSupportedTypes(mContext, mTm)
     }
 
     @Test
     fun testSupportedTypes() {
-        try {
-            mTracker.addSupportedType(supportedTypes[0])
-            fail("Expected IllegalStateException")
-        } catch (expected: IllegalStateException) {}
+        val tracker = makeTracker()
         supportedTypes.forEach {
-            assertTrue(mTracker.isTypeSupported(it))
+            assertTrue(tracker.isTypeSupported(it))
         }
-        assertFalse(mTracker.isTypeSupported(UNSUPPORTED_TYPE))
+        assertFalse(tracker.isTypeSupported(UNSUPPORTED_TYPE))
+    }
+
+    @Test
+    fun testSupportedTypes_NoEthernet() {
+        doReturn(null).`when`(mContext).getSystemService(Context.ETHERNET_SERVICE)
+        assertFalse(makeTracker().isTypeSupported(TYPE_ETHERNET))
+    }
+
+    @Test
+    fun testSupportedTypes_NoTelephony() {
+        doReturn(false).`when`(mTm).isDataCapable
+        val tracker = makeTracker()
+        val nonMobileTypes = arrayOf(TYPE_WIFI, TYPE_WIFI_P2P, TYPE_ETHERNET, TYPE_VPN)
+        nonMobileTypes.forEach {
+            assertTrue(tracker.isTypeSupported(it))
+        }
+        supportedTypes.toSet().minus(nonMobileTypes).forEach {
+            assertFalse(tracker.isTypeSupported(it))
+        }
+    }
+
+    @Test
+    fun testSupportedTypes_NoWifiDirect() {
+        doReturn(false).`when`(mPm).hasSystemFeature(FEATURE_WIFI_DIRECT)
+        val tracker = makeTracker()
+        assertFalse(tracker.isTypeSupported(TYPE_WIFI_P2P))
+        supportedTypes.toSet().minus(TYPE_WIFI_P2P).forEach {
+            assertTrue(tracker.isTypeSupported(it))
+        }
     }
 
     @Test
     fun testSupl() {
+        val tracker = makeTracker()
         val mobileNai = mock(NetworkAgentInfo::class.java)
-        mTracker.add(TYPE_MOBILE, mobileNai)
+        tracker.add(TYPE_MOBILE, mobileNai)
         verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE)
         reset(mMockService)
-        mTracker.add(TYPE_MOBILE_SUPL, mobileNai)
+        tracker.add(TYPE_MOBILE_SUPL, mobileNai)
         verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL)
         reset(mMockService)
-        mTracker.remove(TYPE_MOBILE_SUPL, mobileNai, false /* wasDefault */)
+        tracker.remove(TYPE_MOBILE_SUPL, mobileNai, false /* wasDefault */)
         verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL)
         reset(mMockService)
-        mTracker.add(TYPE_MOBILE_SUPL, mobileNai)
+        tracker.add(TYPE_MOBILE_SUPL, mobileNai)
         verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL)
         reset(mMockService)
-        mTracker.remove(mobileNai, false)
+        tracker.remove(mobileNai, false)
         verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL)
         verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE)
     }
 
     @Test
     fun testAddNetwork() {
+        val tracker = makeTracker()
         val mobileNai = mock(NetworkAgentInfo::class.java)
         val wifiNai = mock(NetworkAgentInfo::class.java)
-        mTracker.add(TYPE_MOBILE, mobileNai)
-        mTracker.add(TYPE_WIFI, wifiNai)
-        assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai)
-        assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+        tracker.add(TYPE_MOBILE, mobileNai)
+        tracker.add(TYPE_WIFI, wifiNai)
+        assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai)
+        assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai)
         // Make sure adding a second NAI does not change the results.
         val secondMobileNai = mock(NetworkAgentInfo::class.java)
-        mTracker.add(TYPE_MOBILE, secondMobileNai)
-        assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai)
-        assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+        tracker.add(TYPE_MOBILE, secondMobileNai)
+        assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai)
+        assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai)
         // Make sure removing a network that wasn't added for this type is a no-op.
-        mTracker.remove(TYPE_MOBILE, wifiNai, false /* wasDefault */)
-        assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai)
-        assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+        tracker.remove(TYPE_MOBILE, wifiNai, false /* wasDefault */)
+        assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai)
+        assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai)
         // Remove the top network for mobile and make sure the second one becomes the network
         // of record for this type.
-        mTracker.remove(TYPE_MOBILE, mobileNai, false /* wasDefault */)
-        assertSame(mTracker.getNetworkForType(TYPE_MOBILE), secondMobileNai)
-        assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+        tracker.remove(TYPE_MOBILE, mobileNai, false /* wasDefault */)
+        assertSame(tracker.getNetworkForType(TYPE_MOBILE), secondMobileNai)
+        assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai)
         // Make sure adding a network for an unsupported type does not register it.
-        mTracker.add(UNSUPPORTED_TYPE, mobileNai)
-        assertNull(mTracker.getNetworkForType(UNSUPPORTED_TYPE))
+        tracker.add(UNSUPPORTED_TYPE, mobileNai)
+        assertNull(tracker.getNetworkForType(UNSUPPORTED_TYPE))
     }
 
     @Test
     fun testBroadcastOnDisconnect() {
+        val tracker = makeTracker()
         val mobileNai1 = mock(NetworkAgentInfo::class.java)
         val mobileNai2 = mock(NetworkAgentInfo::class.java)
         doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai1)
-        mTracker.add(TYPE_MOBILE, mobileNai1)
+        tracker.add(TYPE_MOBILE, mobileNai1)
         verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, CONNECTED, TYPE_MOBILE)
         reset(mMockService)
         doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai2)
-        mTracker.add(TYPE_MOBILE, mobileNai2)
+        tracker.add(TYPE_MOBILE, mobileNai2)
         verify(mMockService, never()).sendLegacyNetworkBroadcast(any(), any(), anyInt())
-        mTracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */)
+        tracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */)
         verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, DISCONNECTED, TYPE_MOBILE)
         verify(mMockService).sendLegacyNetworkBroadcast(mobileNai2, CONNECTED, TYPE_MOBILE)
     }
diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
index 5760211..692c50f 100644
--- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
@@ -18,15 +18,15 @@
 
 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_SPECIFIER;
 import static android.net.NetworkCapabilities.MAX_TRANSPORT;
 import static android.net.NetworkCapabilities.MIN_TRANSPORT;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_FAILURE;
 import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_SUCCESS;
-import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE;
-import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
-import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
 
 import static com.android.testutils.MiscAsserts.assertContainsExactly;
 import static com.android.testutils.MiscAsserts.assertContainsStringsExactly;
@@ -312,14 +312,14 @@
     @Test
     public void testOverrideDefaultMode() throws Exception {
         // Hard-coded default is opportunistic mode.
-        final PrivateDnsConfig cfgAuto = DnsManager.getPrivateDnsConfig(mContentResolver);
+        final PrivateDnsConfig cfgAuto = DnsManager.getPrivateDnsConfig(mCtx);
         assertTrue(cfgAuto.useTls);
         assertEquals("", cfgAuto.hostname);
         assertEquals(new InetAddress[0], cfgAuto.ips);
 
         // Pretend a gservices push sets the default to "off".
         Settings.Global.putString(mContentResolver, PRIVATE_DNS_DEFAULT_MODE, "off");
-        final PrivateDnsConfig cfgOff = DnsManager.getPrivateDnsConfig(mContentResolver);
+        final PrivateDnsConfig cfgOff = DnsManager.getPrivateDnsConfig(mCtx);
         assertFalse(cfgOff.useTls);
         assertEquals("", cfgOff.hostname);
         assertEquals(new InetAddress[0], cfgOff.ips);
@@ -328,7 +328,7 @@
         Settings.Global.putString(
                 mContentResolver, PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
         Settings.Global.putString(mContentResolver, PRIVATE_DNS_SPECIFIER, "strictmode.com");
-        final PrivateDnsConfig cfgStrict = DnsManager.getPrivateDnsConfig(mContentResolver);
+        final PrivateDnsConfig cfgStrict = DnsManager.getPrivateDnsConfig(mCtx);
         assertTrue(cfgStrict.useTls);
         assertEquals("strictmode.com", cfgStrict.hostname);
         assertEquals(new InetAddress[0], cfgStrict.ips);
diff --git a/tests/net/java/com/android/server/connectivity/FullScoreTest.kt b/tests/net/java/com/android/server/connectivity/FullScoreTest.kt
new file mode 100644
index 0000000..eb3b4df
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/FullScoreTest.kt
@@ -0,0 +1,134 @@
+/*
+ * 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.net.NetworkAgentConfig
+import android.net.NetworkCapabilities
+import android.text.TextUtils
+import android.util.ArraySet
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.server.connectivity.FullScore.MAX_CS_MANAGED_POLICY
+import com.android.server.connectivity.FullScore.POLICY_ACCEPT_UNVALIDATED
+import com.android.server.connectivity.FullScore.POLICY_EVER_USER_SELECTED
+import com.android.server.connectivity.FullScore.POLICY_IS_VALIDATED
+import com.android.server.connectivity.FullScore.POLICY_IS_VPN
+import org.junit.Test
+import org.junit.runner.RunWith
+import kotlin.collections.minOfOrNull
+import kotlin.collections.maxOfOrNull
+import kotlin.reflect.full.staticProperties
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class FullScoreTest {
+    // Convenience methods
+    fun FullScore.withPolicies(
+        validated: Boolean = false,
+        vpn: Boolean = false,
+        onceChosen: Boolean = false,
+        acceptUnvalidated: Boolean = false
+    ): FullScore {
+        val nac = NetworkAgentConfig.Builder().apply {
+            setUnvalidatedConnectivityAcceptable(acceptUnvalidated)
+            setExplicitlySelected(onceChosen)
+        }.build()
+        val nc = NetworkCapabilities.Builder().apply {
+            if (vpn) addTransportType(NetworkCapabilities.TRANSPORT_VPN)
+            if (validated) addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
+        }.build()
+        return mixInScore(nc, nac)
+    }
+
+    @Test
+    fun testGetLegacyInt() {
+        val ns = FullScore(50, 0L /* policy */)
+        assertEquals(10, ns.legacyInt) // -40 penalty for not being validated
+        assertEquals(50, ns.legacyIntAsValidated)
+
+        val vpnNs = FullScore(101, 0L /* policy */).withPolicies(vpn = true)
+        assertEquals(101, vpnNs.legacyInt) // VPNs are not subject to unvalidation penalty
+        assertEquals(101, vpnNs.legacyIntAsValidated)
+        assertEquals(101, vpnNs.withPolicies(validated = true).legacyInt)
+        assertEquals(101, vpnNs.withPolicies(validated = true).legacyIntAsValidated)
+
+        val validatedNs = ns.withPolicies(validated = true)
+        assertEquals(50, validatedNs.legacyInt) // No penalty, this is validated
+        assertEquals(50, validatedNs.legacyIntAsValidated)
+
+        val chosenNs = ns.withPolicies(onceChosen = true)
+        assertEquals(10, chosenNs.legacyInt)
+        assertEquals(100, chosenNs.legacyIntAsValidated)
+        assertEquals(10, chosenNs.withPolicies(acceptUnvalidated = true).legacyInt)
+        assertEquals(50, chosenNs.withPolicies(acceptUnvalidated = true).legacyIntAsValidated)
+    }
+
+    @Test
+    fun testToString() {
+        val string = FullScore(10, 0L /* policy */)
+                .withPolicies(vpn = true, acceptUnvalidated = true).toString()
+        assertTrue(string.contains("Score(10"), string)
+        assertTrue(string.contains("ACCEPT_UNVALIDATED"), string)
+        assertTrue(string.contains("IS_VPN"), string)
+        assertFalse(string.contains("IS_VALIDATED"), string)
+        val foundNames = ArraySet<String>()
+        getAllPolicies().forEach {
+            val name = FullScore.policyNameOf(it.get() as Int)
+            assertFalse(TextUtils.isEmpty(name))
+            assertFalse(foundNames.contains(name))
+            foundNames.add(name)
+        }
+        assertFailsWith<IllegalArgumentException> {
+            FullScore.policyNameOf(MAX_CS_MANAGED_POLICY + 1)
+        }
+    }
+
+    fun getAllPolicies() = Regex("POLICY_.*").let { nameRegex ->
+        FullScore::class.staticProperties.filter { it.name.matches(nameRegex) }
+    }
+
+    @Test
+    fun testHasPolicy() {
+        val ns = FullScore(50, 0L /* policy */)
+        assertFalse(ns.hasPolicy(POLICY_IS_VALIDATED))
+        assertFalse(ns.hasPolicy(POLICY_IS_VPN))
+        assertFalse(ns.hasPolicy(POLICY_EVER_USER_SELECTED))
+        assertFalse(ns.hasPolicy(POLICY_ACCEPT_UNVALIDATED))
+        assertTrue(ns.withPolicies(validated = true).hasPolicy(POLICY_IS_VALIDATED))
+        assertTrue(ns.withPolicies(vpn = true).hasPolicy(POLICY_IS_VPN))
+        assertTrue(ns.withPolicies(onceChosen = true).hasPolicy(POLICY_EVER_USER_SELECTED))
+        assertTrue(ns.withPolicies(acceptUnvalidated = true).hasPolicy(POLICY_ACCEPT_UNVALIDATED))
+    }
+
+    @Test
+    fun testMinMaxPolicyConstants() {
+        val policies = getAllPolicies()
+
+        policies.forEach { policy ->
+            assertTrue(policy.get() as Int >= FullScore.MIN_CS_MANAGED_POLICY)
+            assertTrue(policy.get() as Int <= FullScore.MAX_CS_MANAGED_POLICY)
+        }
+        assertEquals(FullScore.MIN_CS_MANAGED_POLICY,
+                policies.minOfOrNull { it.get() as Int })
+        assertEquals(FullScore.MAX_CS_MANAGED_POLICY,
+                policies.maxOfOrNull { it.get() as Int })
+    }
+}
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index a913673..9ab60a4 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -40,6 +40,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkProvider;
+import android.net.NetworkScore;
 import android.os.Binder;
 import android.text.format.DateUtils;
 
@@ -355,9 +356,10 @@
         caps.addCapability(0);
         caps.addTransportType(transport);
         NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info,
-                new LinkProperties(), caps, 50, mCtx, null, new NetworkAgentConfig() /* config */,
-                mConnService, mNetd, mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(),
-                mQosCallbackTracker);
+                new LinkProperties(), caps, new NetworkScore.Builder().setLegacyInt(50).build(),
+                mCtx, null, new NetworkAgentConfig.Builder().build(), mConnService, mNetd,
+                mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(),
+                mQosCallbackTracker, new ConnectivityService.Dependencies());
         nai.everValidated = true;
         return nai;
     }
diff --git a/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java b/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
index 950d7163..38f6d7f 100644
--- a/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
+++ b/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
@@ -46,12 +46,12 @@
 import android.content.pm.ApplicationInfo;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
+import android.net.EthernetNetworkSpecifier;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkTemplate;
-import android.net.StringNetworkSpecifier;
 import android.net.TelephonyNetworkSpecifier;
 import android.os.Handler;
 import android.os.UserHandle;
@@ -240,7 +240,7 @@
         NetworkCapabilities capabilities = new NetworkCapabilities()
                 .addCapability(NET_CAPABILITY_INTERNET)
                 .addTransportType(TRANSPORT_CELLULAR)
-                .setNetworkSpecifier(new StringNetworkSpecifier("234"));
+                .setNetworkSpecifier(new EthernetNetworkSpecifier("eth234"));
         if (!roaming) {
             capabilities.addCapability(NET_CAPABILITY_NOT_ROAMING);
         }
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
index 5f56e25..9b2a638 100644
--- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -16,11 +16,15 @@
 
 package com.android.server.connectivity;
 
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -34,6 +38,7 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkAgentConfig;
+import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.os.Handler;
 import android.os.test.TestLooper;
@@ -72,11 +77,15 @@
     Handler mHandler;
     NetworkAgentConfig mAgentConfig = new NetworkAgentConfig();
 
-    Nat464Xlat makeNat464Xlat() {
-        return new Nat464Xlat(mNai, mNetd, mDnsResolver) {
+    Nat464Xlat makeNat464Xlat(boolean isCellular464XlatEnabled) {
+        return new Nat464Xlat(mNai, mNetd, mDnsResolver, new ConnectivityService.Dependencies()) {
             @Override protected int getNetId() {
                 return NETID;
             }
+
+            @Override protected boolean isCellular464XlatEnabled() {
+                return isCellular464XlatEnabled;
+            }
         };
     }
 
@@ -99,6 +108,7 @@
         mNai.linkProperties.setInterfaceName(BASE_IFACE);
         mNai.networkInfo = new NetworkInfo(null);
         mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI);
+        mNai.networkCapabilities = new NetworkCapabilities();
         markNetworkConnected();
         when(mNai.connService()).thenReturn(mConnectivity);
         when(mNai.netAgentConfig()).thenReturn(mAgentConfig);
@@ -110,21 +120,23 @@
     }
 
     private void assertRequiresClat(boolean expected, NetworkAgentInfo nai) {
+        Nat464Xlat nat = makeNat464Xlat(true);
         String msg = String.format("requiresClat expected %b for type=%d state=%s skip=%b "
                 + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
                 nai.networkInfo.getDetailedState(),
                 mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
                 nai.linkProperties.getLinkAddresses());
-        assertEquals(msg, expected, Nat464Xlat.requiresClat(nai));
+        assertEquals(msg, expected, nat.requiresClat(nai));
     }
 
     private void assertShouldStartClat(boolean expected, NetworkAgentInfo nai) {
+        Nat464Xlat nat = makeNat464Xlat(true);
         String msg = String.format("shouldStartClat expected %b for type=%d state=%s skip=%b "
                 + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
                 nai.networkInfo.getDetailedState(),
                 mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
                 nai.linkProperties.getLinkAddresses());
-        assertEquals(msg, expected, Nat464Xlat.shouldStartClat(nai));
+        assertEquals(msg, expected, nat.shouldStartClat(nai));
     }
 
     @Test
@@ -194,7 +206,7 @@
     }
 
     private void checkNormalStartAndStop(boolean dueToDisconnect) throws Exception {
-        Nat464Xlat nat = makeNat464Xlat();
+        Nat464Xlat nat = makeNat464Xlat(true);
         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
 
         mNai.linkProperties.addLinkAddress(V6ADDR);
@@ -245,7 +257,7 @@
     }
 
     private void checkStartStopStart(boolean interfaceRemovedFirst) throws Exception {
-        Nat464Xlat nat = makeNat464Xlat();
+        Nat464Xlat nat = makeNat464Xlat(true);
         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
         InOrder inOrder = inOrder(mNetd, mConnectivity);
 
@@ -335,7 +347,7 @@
 
     @Test
     public void testClatdCrashWhileRunning() throws Exception {
-        Nat464Xlat nat = makeNat464Xlat();
+        Nat464Xlat nat = makeNat464Xlat(true);
         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
 
         nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
@@ -372,7 +384,7 @@
     }
 
     private void checkStopBeforeClatdStarts(boolean dueToDisconnect) throws Exception {
-        Nat464Xlat nat = makeNat464Xlat();
+        Nat464Xlat nat = makeNat464Xlat(true);
 
         mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
 
@@ -414,7 +426,7 @@
     }
 
     private void checkStopAndClatdNeverStarts(boolean dueToDisconnect) throws Exception {
-        Nat464Xlat nat = makeNat464Xlat();
+        Nat464Xlat nat = makeNat464Xlat(true);
 
         mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
 
@@ -450,7 +462,7 @@
         final IpPrefix prefixFromDns = new IpPrefix(NAT64_PREFIX);
         final IpPrefix prefixFromRa = new IpPrefix(OTHER_NAT64_PREFIX);
 
-        Nat464Xlat nat = makeNat464Xlat();
+        Nat464Xlat nat = makeNat464Xlat(true);
 
         final LinkProperties emptyLp = new LinkProperties();
         LinkProperties fixedupLp;
@@ -486,10 +498,57 @@
         assertEquals(null, fixedupLp.getNat64Prefix());
     }
 
+    private void checkClatDisabledOnCellular(boolean onCellular) throws Exception {
+        // Disable 464xlat on cellular networks.
+        Nat464Xlat nat = makeNat464Xlat(false);
+        mNai.linkProperties.addLinkAddress(V6ADDR);
+        mNai.networkCapabilities.setTransportType(TRANSPORT_CELLULAR, onCellular);
+        nat.update();
+
+        final IpPrefix nat64Prefix = new IpPrefix(NAT64_PREFIX);
+        if (onCellular) {
+            // Prefix discovery is never started.
+            verify(mDnsResolver, never()).startPrefix64Discovery(eq(NETID));
+            assertIdle(nat);
+
+            // If a NAT64 prefix comes in from an RA, clat is not started either.
+            mNai.linkProperties.setNat64Prefix(nat64Prefix);
+            nat.setNat64PrefixFromRa(nat64Prefix);
+            nat.update();
+            verify(mNetd, never()).clatdStart(anyString(), anyString());
+            assertIdle(nat);
+        } else {
+            // Prefix discovery is started.
+            verify(mDnsResolver).startPrefix64Discovery(eq(NETID));
+            assertIdle(nat);
+
+            // If a NAT64 prefix comes in from an RA, clat is started.
+            mNai.linkProperties.setNat64Prefix(nat64Prefix);
+            nat.setNat64PrefixFromRa(nat64Prefix);
+            nat.update();
+            verify(mNetd).clatdStart(BASE_IFACE, NAT64_PREFIX);
+            assertStarting(nat);
+        }
+    }
+
+    @Test
+    public void testClatDisabledOnCellular() throws Exception {
+        checkClatDisabledOnCellular(true);
+    }
+
+    @Test
+    public void testClatDisabledOnNonCellular() throws Exception {
+        checkClatDisabledOnCellular(false);
+    }
+
     static void assertIdle(Nat464Xlat nat) {
         assertTrue("Nat464Xlat was not IDLE", !nat.isStarted());
     }
 
+    static void assertStarting(Nat464Xlat nat) {
+        assertTrue("Nat464Xlat was not STARTING", nat.isStarting());
+    }
+
     static void assertRunning(Nat464Xlat nat) {
         assertTrue("Nat464Xlat was not RUNNING", nat.isRunning());
     }
diff --git a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
index ff8c632..3adf08c 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
@@ -20,6 +20,7 @@
 
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
@@ -35,6 +36,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.net.ConnectivityResources;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.os.UserHandle;
@@ -43,9 +45,10 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.R;
+import com.android.connectivity.resources.R;
 import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
 
+import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -119,11 +122,25 @@
         when(mCtx.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
                 .thenReturn(mNotificationManager);
         when(mNetworkInfo.getExtraInfo()).thenReturn(TEST_EXTRA_INFO);
+        ConnectivityResources.setResourcesContextForTest(mCtx);
         when(mResources.getColor(anyInt(), any())).thenReturn(0xFF607D8B);
 
+        // Come up with some credible-looking transport names. The actual values do not matter.
+        String[] transportNames = new String[NetworkCapabilities.MAX_TRANSPORT + 1];
+        for (int transport = 0; transport <= NetworkCapabilities.MAX_TRANSPORT; transport++) {
+            transportNames[transport] = NetworkCapabilities.transportNameOf(transport);
+        }
+        when(mResources.getStringArray(R.array.network_switch_type_name))
+            .thenReturn(transportNames);
+
         mManager = new NetworkNotificationManager(mCtx, mTelephonyManager);
     }
 
+    @After
+    public void tearDown() {
+        ConnectivityResources.setResourcesContextForTest(null);
+    }
+
     private void verifyTitleByNetwork(final int id, final NetworkAgentInfo nai, final int title) {
         final String tag = NetworkNotificationManager.tagFor(id);
         mManager.showNotification(id, PRIVATE_DNS_BROKEN, nai, null, null, true);
@@ -142,15 +159,15 @@
     public void testTitleOfPrivateDnsBroken() {
         // Test the title of mobile data.
         verifyTitleByNetwork(100, mCellNai, R.string.mobile_no_internet);
-        reset(mResources);
+        clearInvocations(mResources);
 
         // Test the title of wifi.
         verifyTitleByNetwork(101, mWifiNai, R.string.wifi_no_internet);
-        reset(mResources);
+        clearInvocations(mResources);
 
         // Test the title of other networks.
         verifyTitleByNetwork(102, mVpnNai, R.string.other_networks_no_internet);
-        reset(mResources);
+        clearInvocations(mResources);
     }
 
     @Test
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index e4e24b4..fec5ef3 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -48,18 +48,22 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.net.INetd;
 import android.net.UidRange;
+import android.net.Uri;
 import android.os.Build;
 import android.os.SystemConfigManager;
 import android.os.UserHandle;
@@ -70,12 +74,11 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.server.LocalServices;
-import com.android.server.pm.PackageList;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.AdditionalAnswers;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
@@ -112,7 +115,6 @@
     @Mock private Context mContext;
     @Mock private PackageManager mPackageManager;
     @Mock private INetd mNetdService;
-    @Mock private PackageManagerInternal mMockPmi;
     @Mock private UserManager mUserManager;
     @Mock private PermissionMonitor.Dependencies mDeps;
     @Mock private SystemConfigManager mSystemConfigManager;
@@ -131,16 +133,14 @@
         when(mContext.getSystemService(Context.SYSTEM_CONFIG_SERVICE))
                 .thenReturn(mSystemConfigManager);
         when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]);
+        final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mContext));
+        doReturn(UserHandle.ALL).when(asUserCtx).getUser();
+        when(mContext.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx);
 
         mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps));
 
-        LocalServices.removeServiceForTest(PackageManagerInternal.class);
-        LocalServices.addService(PackageManagerInternal.class, mMockPmi);
-        when(mMockPmi.getPackageList(any())).thenReturn(new PackageList(new ArrayList<String>(),
-                  /* observer */ null));
         when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(/* empty app list */ null);
         mPermissionMonitor.startMonitoring();
-        verify(mMockPmi).getPackageList(mPermissionMonitor);
     }
 
     private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion, int uid,
@@ -770,4 +770,32 @@
                 INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS,
                 new int[]{ MOCK_UID2 });
     }
+
+    @Test
+    public void testIntentReceiver() throws Exception {
+        final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
+        final ArgumentCaptor<BroadcastReceiver> receiverCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        verify(mContext, times(1)).registerReceiver(receiverCaptor.capture(), any(), any(), any());
+        final BroadcastReceiver receiver = receiverCaptor.getValue();
+
+        // Verify receiving PACKAGE_ADDED intent.
+        final Intent addedIntent = new Intent(Intent.ACTION_PACKAGE_ADDED,
+                Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */));
+        addedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1);
+        setPackagePermissions(MOCK_PACKAGE1, MOCK_UID1,
+                new String[] { INTERNET, UPDATE_DEVICE_STATS });
+        receiver.onReceive(mContext, addedIntent);
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+                | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[] { MOCK_UID1 });
+
+        // Verify receiving PACKAGE_REMOVED intent.
+        when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(null);
+        final Intent removedIntent = new Intent(Intent.ACTION_PACKAGE_REMOVED,
+                Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */));
+        removedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1);
+        receiver.onReceive(mContext, removedIntent);
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[] { MOCK_UID1 });
+    }
+
 }
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index b8f7fbc..6ad4900 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -23,6 +23,7 @@
 import static android.net.ConnectivityManager.NetworkCallback;
 import static android.net.INetd.IF_STATE_DOWN;
 import static android.net.INetd.IF_STATE_UP;
+import static android.os.UserHandle.PER_USER_RANGE;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -74,7 +75,6 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo.DetailedState;
 import android.net.RouteInfo;
-import android.net.UidRange;
 import android.net.UidRangeParcel;
 import android.net.VpnManager;
 import android.net.VpnService;
@@ -181,8 +181,7 @@
             mPackages.put(PKGS[i], PKG_UIDS[i]);
         }
     }
-    private static final UidRange PRI_USER_RANGE =
-            UidRange.createForUser(UserHandle.of(primaryUser.id));
+    private static final Range<Integer> PRI_USER_RANGE = uidRangeForUser(primaryUser.id);
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Context mContext;
     @Mock private UserManager mUserManager;
@@ -260,6 +259,21 @@
                 .thenReturn(tunnelResp);
     }
 
+    private Set<Range<Integer>> rangeSet(Range<Integer> ... ranges) {
+        final Set<Range<Integer>> range = new ArraySet<>();
+        for (Range<Integer> r : ranges) range.add(r);
+
+        return range;
+    }
+
+    private static Range<Integer> uidRangeForUser(int userId) {
+        return new Range<Integer>(userId * PER_USER_RANGE, (userId + 1) * PER_USER_RANGE - 1);
+    }
+
+    private Range<Integer> uidRange(int start, int stop) {
+        return new Range<Integer>(start, stop);
+    }
+
     @Test
     public void testRestrictedProfilesAreAddedToVpn() {
         setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB);
@@ -268,12 +282,10 @@
 
         // Assume the user can have restricted profiles.
         doReturn(true).when(mUserManager).canHaveRestrictedProfile();
-        final Set<UidRange> ranges =
+        final Set<Range<Integer>> ranges =
                 vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, null, null);
 
-        assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
-                PRI_USER_RANGE, UidRange.createForUser(UserHandle.of(restrictedProfileA.id))
-        })), ranges);
+        assertEquals(rangeSet(PRI_USER_RANGE, uidRangeForUser(restrictedProfileA.id)), ranges);
     }
 
     @Test
@@ -281,10 +293,10 @@
         setMockedUsers(primaryUser, managedProfileA);
 
         final Vpn vpn = createVpn(primaryUser.id);
-        final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
+        final Set<Range<Integer>> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
                 null, null);
 
-        assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] { PRI_USER_RANGE })), ranges);
+        assertEquals(rangeSet(PRI_USER_RANGE), ranges);
     }
 
     @Test
@@ -292,35 +304,38 @@
         setMockedUsers(primaryUser, restrictedProfileA, managedProfileA);
 
         final Vpn vpn = createVpn(primaryUser.id);
-        final Set<UidRange> ranges = new ArraySet<>();
+        final Set<Range<Integer>> ranges = new ArraySet<>();
         vpn.addUserToRanges(ranges, primaryUser.id, null, null);
 
-        assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] { PRI_USER_RANGE })), ranges);
+        assertEquals(rangeSet(PRI_USER_RANGE), ranges);
     }
 
     @Test
     public void testUidAllowAndDenylist() throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
-        final UidRange user = PRI_USER_RANGE;
+        final Range<Integer> user = PRI_USER_RANGE;
+        final int userStart = user.getLower();
+        final int userStop = user.getUpper();
         final String[] packages = {PKGS[0], PKGS[1], PKGS[2]};
 
         // Allowed list
-        final Set<UidRange> allow = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
-                Arrays.asList(packages), null);
-        assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
-            new UidRange(user.start + PKG_UIDS[0], user.start + PKG_UIDS[0]),
-            new UidRange(user.start + PKG_UIDS[1], user.start + PKG_UIDS[2])
-        })), allow);
+        final Set<Range<Integer>> allow = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
+                Arrays.asList(packages), null /* disallowedApplications */);
+        assertEquals(rangeSet(
+                uidRange(userStart + PKG_UIDS[0], userStart + PKG_UIDS[0]),
+                uidRange(userStart + PKG_UIDS[1], userStart + PKG_UIDS[2])),
+                allow);
 
         // Denied list
-        final Set<UidRange> disallow = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
-                null, Arrays.asList(packages));
-        assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
-            new UidRange(user.start, user.start + PKG_UIDS[0] - 1),
-            new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
-            /* Empty range between UIDS[1] and UIDS[2], should be excluded, */
-            new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
-        })), disallow);
+        final Set<Range<Integer>> disallow =
+                vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
+                        null /* allowedApplications */, Arrays.asList(packages));
+        assertEquals(rangeSet(
+                uidRange(userStart, userStart + PKG_UIDS[0] - 1),
+                uidRange(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[1] - 1),
+                /* Empty range between UIDS[1] and UIDS[2], should be excluded, */
+                uidRange(userStart + PKG_UIDS[2] + 1, userStop)),
+                disallow);
     }
 
     @Test
@@ -350,84 +365,86 @@
     @Test
     public void testLockdownChangingPackage() throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
-        final UidRange user = PRI_USER_RANGE;
-
+        final Range<Integer> user = PRI_USER_RANGE;
+        final int userStart = user.getLower();
+        final int userStop = user.getUpper();
         // Set always-on without lockdown.
         assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null));
 
         // Set always-on with lockdown.
         assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null));
         verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
-                new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
-                new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
+                new UidRangeParcel(userStart, userStart + PKG_UIDS[1] - 1),
+                new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStop)
         }));
 
         // Switch to another app.
         assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null));
         verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
-                new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
-                new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
+                new UidRangeParcel(userStart, userStart + PKG_UIDS[1] - 1),
+                new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStop)
         }));
         verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
-                new UidRangeParcel(user.start, user.start + PKG_UIDS[3] - 1),
-                new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
+                new UidRangeParcel(userStart, userStart + PKG_UIDS[3] - 1),
+                new UidRangeParcel(userStart + PKG_UIDS[3] + 1, userStop)
         }));
     }
 
     @Test
     public void testLockdownAllowlist() throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
-        final UidRange user = PRI_USER_RANGE;
-
+        final Range<Integer> user = PRI_USER_RANGE;
+        final int userStart = user.getLower();
+        final int userStop = user.getUpper();
         // Set always-on with lockdown and allow app PKGS[2] from lockdown.
         assertTrue(vpn.setAlwaysOnPackage(
                 PKGS[1], true, Collections.singletonList(PKGS[2])));
-        verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
-                new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
-                new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop)
+        verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[]  {
+                new UidRangeParcel(userStart, userStart + PKG_UIDS[1] - 1),
+                new UidRangeParcel(userStart + PKG_UIDS[2] + 1, userStop)
         }));
         // Change allowed app list to PKGS[3].
         assertTrue(vpn.setAlwaysOnPackage(
                 PKGS[1], true, Collections.singletonList(PKGS[3])));
         verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
-                new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop)
+                new UidRangeParcel(userStart + PKG_UIDS[2] + 1, userStop)
         }));
         verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
-                new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1),
-                new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
+                new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStart + PKG_UIDS[3] - 1),
+                new UidRangeParcel(userStart + PKG_UIDS[3] + 1, userStop)
         }));
 
         // Change the VPN app.
         assertTrue(vpn.setAlwaysOnPackage(
                 PKGS[0], true, Collections.singletonList(PKGS[3])));
         verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
-                new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
-                new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1)
+                new UidRangeParcel(userStart, userStart + PKG_UIDS[1] - 1),
+                new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStart + PKG_UIDS[3] - 1)
         }));
         verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
-                new UidRangeParcel(user.start, user.start + PKG_UIDS[0] - 1),
-                new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1)
+                new UidRangeParcel(userStart, userStart + PKG_UIDS[0] - 1),
+                new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[3] - 1)
         }));
 
         // Remove the list of allowed packages.
         assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null));
         verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
-                new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1),
-                new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
+                new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[3] - 1),
+                new UidRangeParcel(userStart + PKG_UIDS[3] + 1, userStop)
         }));
         verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
-                new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.stop),
+                new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStop),
         }));
 
         // Add the list of allowed packages.
         assertTrue(vpn.setAlwaysOnPackage(
                 PKGS[0], true, Collections.singletonList(PKGS[1])));
         verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
-                new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.stop)
+                new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStop)
         }));
         verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
-                new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
-                new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
+                new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[1] - 1),
+                new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStop)
         }));
 
         // Try allowing a package with a comma, should be rejected.
@@ -439,12 +456,12 @@
         assertTrue(vpn.setAlwaysOnPackage(
                 PKGS[0], true, Arrays.asList("com.foo.app", PKGS[2], "com.bar.app")));
         verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
-                new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
-                new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
+                new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[1] - 1),
+                new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStop)
         }));
         verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
-                new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[2] - 1),
-                new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop)
+                new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[2] - 1),
+                new UidRangeParcel(userStart + PKG_UIDS[2] + 1, userStop)
         }));
     }
 
@@ -452,7 +469,7 @@
     public void testLockdownRuleRepeatability() throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] {
-                new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)};
+                new UidRangeParcel(PRI_USER_RANGE.getLower(), PRI_USER_RANGE.getUpper())};
         // Given legacy lockdown is already enabled,
         vpn.setLockdown(true);
         verify(mConnectivityManager, times(1)).setRequireVpnForUids(true,
@@ -484,7 +501,7 @@
     public void testLockdownRuleReversibility() throws Exception {
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRangeParcel[] entireUser = {
-            new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)
+            new UidRangeParcel(PRI_USER_RANGE.getLower(), PRI_USER_RANGE.getUpper())
         };
         final UidRangeParcel[] exceptPkg0 = {
             new UidRangeParcel(entireUser[0].start, entireUser[0].start + PKG_UIDS[0] - 1),
@@ -1026,7 +1043,11 @@
             .thenReturn(new Network[] { new Network(101) });
 
         when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(),
-                anyInt(), any(), anyInt())).thenReturn(new Network(102));
+                any(), any(), anyInt())).thenAnswer(invocation -> {
+                    // The runner has registered an agent and is now ready.
+                    legacyRunnerReady.open();
+                    return new Network(102);
+                });
         final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile);
         final TestDeps deps = (TestDeps) vpn.mDeps;
         try {
@@ -1048,7 +1069,7 @@
             ArgumentCaptor<NetworkCapabilities> ncCaptor =
                     ArgumentCaptor.forClass(NetworkCapabilities.class);
             verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(),
-                    lpCaptor.capture(), ncCaptor.capture(), anyInt(), any(), anyInt());
+                    lpCaptor.capture(), ncCaptor.capture(), any(), any(), anyInt());
 
             // In this test the expected address is always v4 so /32.
             // Note that the interface needs to be specified because RouteInfo objects stored in
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 9334e2c..eeeb4fb 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -89,6 +89,7 @@
 import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
 import android.net.UnderlyingNetworkInfo;
+import android.net.TelephonyNetworkSpecifier;
 import android.net.netstats.provider.INetworkStatsProviderCallback;
 import android.os.ConditionVariable;
 import android.os.Handler;
@@ -1280,6 +1281,77 @@
     }
 
     @Test
+    public void testDualVilteProviderStats() throws Exception {
+        // Pretend that network comes online.
+        expectDefaultSettings();
+        final int subId1 = 1;
+        final int subId2 = 2;
+        final NetworkStateSnapshot[] states = new NetworkStateSnapshot[]{
+                buildImsState(IMSI_1, subId1, TEST_IFACE),
+                buildImsState(IMSI_2, subId2, TEST_IFACE2)};
+        expectNetworkStatsSummary(buildEmptyStats());
+        expectNetworkStatsUidDetail(buildEmptyStats());
+
+        // Register custom provider and retrieve callback.
+        final TestableNetworkStatsProviderBinder provider =
+                new TestableNetworkStatsProviderBinder();
+        final INetworkStatsProviderCallback cb =
+                mService.registerNetworkStatsProvider("TEST", provider);
+        assertNotNull(cb);
+
+        mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
+                new UnderlyingNetworkInfo[0]);
+
+        // Verifies that one requestStatsUpdate will be called during iface update.
+        provider.expectOnRequestStatsUpdate(0 /* unused */);
+
+        // Create some initial traffic and report to the service.
+        incrementCurrentTime(HOUR_IN_MILLIS);
+        final String vtIface1 = NetworkStats.IFACE_VT + subId1;
+        final String vtIface2 = NetworkStats.IFACE_VT + subId2;
+        final NetworkStats expectedStats = new NetworkStats(0L, 1)
+                .addEntry(new NetworkStats.Entry(vtIface1, UID_RED, SET_DEFAULT,
+                        TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES,
+                        128L, 2L, 128L, 2L, 1L))
+                .addEntry(new NetworkStats.Entry(vtIface2, UID_RED, SET_DEFAULT,
+                        TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES,
+                        64L, 1L, 64L, 1L, 1L));
+        cb.notifyStatsUpdated(0 /* unused */, expectedStats, expectedStats);
+
+        // Make another empty mutable stats object. This is necessary since the new NetworkStats
+        // object will be used to compare with the old one in NetworkStatsRecoder, two of them
+        // cannot be the same object.
+        expectNetworkStatsUidDetail(buildEmptyStats());
+
+        forcePollAndWaitForIdle();
+
+        // Verifies that one requestStatsUpdate and setAlert will be called during polling.
+        provider.expectOnRequestStatsUpdate(0 /* unused */);
+        provider.expectOnSetAlert(MB_IN_BYTES);
+
+        // Verifies that service recorded history, does not verify uid tag part.
+        assertUidTotal(sTemplateImsi1, UID_RED, 128L, 2L, 128L, 2L, 1);
+
+        // Verifies that onStatsUpdated updates the stats accordingly.
+        NetworkStats stats = mSession.getSummaryForAllUid(
+                sTemplateImsi1, Long.MIN_VALUE, Long.MAX_VALUE, true);
+        assertEquals(1, stats.size());
+        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 1L);
+
+        stats = mSession.getSummaryForAllUid(
+                sTemplateImsi2, Long.MIN_VALUE, Long.MAX_VALUE, true);
+        assertEquals(1, stats.size());
+        assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1L);
+
+        // Verifies that unregister the callback will remove the provider from service.
+        cb.unregister();
+        forcePollAndWaitForIdle();
+        provider.assertNoCallback();
+    }
+
+    @Test
     public void testStatsProviderSetAlert() throws Exception {
         // Pretend that network comes online.
         expectDefaultSettings();
@@ -1616,6 +1688,20 @@
                 TYPE_MOBILE);
     }
 
+    private static NetworkStateSnapshot buildImsState(
+            String subscriberId, int subId, String ifaceName) {
+        final LinkProperties prop = new LinkProperties();
+        prop.setInterfaceName(ifaceName);
+        final NetworkCapabilities capabilities = new NetworkCapabilities();
+        capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, true);
+        capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
+        capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_IMS, true);
+        capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+        capabilities.setNetworkSpecifier(new TelephonyNetworkSpecifier(subId));
+        return new NetworkStateSnapshot(
+                MOBILE_NETWORK, capabilities, prop, subscriberId, TYPE_MOBILE);
+    }
+
     private long getElapsedRealtime() {
         return mElapsedRealtime;
     }
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 5b17aad..8a0c923 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -109,16 +109,6 @@
     }
 
     @Test
-    public void testBuilderRequiresNonEmptyUnderlyingCaps() {
-        try {
-            newBuilder().addExposedCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).build();
-
-            fail("Expected exception due to invalid required underlying capabilities");
-        } catch (IllegalArgumentException e) {
-        }
-    }
-
-    @Test
     public void testBuilderRequiresNonNullRetryInterval() {
         try {
             newBuilder().setRetryInterval(null);
diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
index 7515971..516c206 100644
--- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
@@ -204,7 +204,7 @@
                 new VcnStatusCallbackBinder(INLINE_EXECUTOR, mMockStatusCallback);
 
         cbBinder.onVcnStatusChanged(VCN_STATUS_CODE_ACTIVE);
-        verify(mMockStatusCallback).onVcnStatusChanged(VCN_STATUS_CODE_ACTIVE);
+        verify(mMockStatusCallback).onStatusChanged(VCN_STATUS_CODE_ACTIVE);
 
         cbBinder.onGatewayConnectionError(
                 UNDERLYING_NETWORK_CAPABILITIES,
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 11498de..f15d420 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -19,6 +19,8 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
 
 import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
@@ -32,9 +34,8 @@
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.argThat;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
@@ -48,7 +49,9 @@
 
 import android.annotation.NonNull;
 import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
 import android.net.ConnectivityManager;
 import android.net.LinkProperties;
 import android.net.NetworkCapabilities;
@@ -74,7 +77,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.util.LocationPermissionChecker;
+import com.android.net.module.util.LocationPermissionChecker;
 import com.android.server.VcnManagementService.VcnCallback;
 import com.android.server.VcnManagementService.VcnStatusCallbackInfo;
 import com.android.server.vcn.TelephonySubscriptionTracker;
@@ -239,9 +242,14 @@
         doReturn(Collections.singletonList(TEST_SUBSCRIPTION_INFO))
                 .when(mSubMgr)
                 .getSubscriptionsInGroup(any());
-        doReturn(isPrivileged)
+        doReturn(mTelMgr)
                 .when(mTelMgr)
-                .hasCarrierPrivileges(eq(TEST_SUBSCRIPTION_INFO.getSubscriptionId()));
+                .createForSubscriptionId(eq(TEST_SUBSCRIPTION_INFO.getSubscriptionId()));
+        doReturn(isPrivileged
+                        ? CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
+                        : CARRIER_PRIVILEGE_STATUS_NO_ACCESS)
+                .when(mTelMgr)
+                .checkCarrierPrivilegesForPackage(eq(TEST_PACKAGE_NAME));
     }
 
     @Test
@@ -330,6 +338,13 @@
         return captor.getValue();
     }
 
+    private BroadcastReceiver getPackageChangeReceiver() {
+        final ArgumentCaptor<BroadcastReceiver> captor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        verify(mMockContext).registerReceiver(captor.capture(), any(), any(), any());
+        return captor.getValue();
+    }
+
     private Vcn startAndGetVcnInstance(ParcelUuid uuid) {
         mVcnMgmtSvc.setVcnConfig(uuid, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
         return mVcnMgmtSvc.getAllVcns().get(uuid);
@@ -392,7 +407,7 @@
         mTestLooper.moveTimeForward(
                 VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2);
         mTestLooper.dispatchAll();
-        mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2);
+        mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2, TEST_PACKAGE_NAME);
         final Vcn newInstance = startAndGetVcnInstance(TEST_UUID_2);
 
         // Verify that new instance was different, and the old one was torn down
@@ -406,6 +421,42 @@
     }
 
     @Test
+    public void testPackageChangeListenerRegistered() throws Exception {
+        verify(mMockContext).registerReceiver(any(BroadcastReceiver.class), argThat(filter -> {
+            return filter.hasAction(Intent.ACTION_PACKAGE_ADDED)
+                    && filter.hasAction(Intent.ACTION_PACKAGE_REPLACED)
+                    && filter.hasAction(Intent.ACTION_PACKAGE_REMOVED);
+        }), any(), any());
+    }
+
+    @Test
+    public void testPackageChangeListener_packageAdded() throws Exception {
+        final BroadcastReceiver receiver = getPackageChangeReceiver();
+
+        verify(mMockContext).registerReceiver(any(), argThat(filter -> {
+            return filter.hasAction(Intent.ACTION_PACKAGE_ADDED)
+                    && filter.hasAction(Intent.ACTION_PACKAGE_REPLACED)
+                    && filter.hasAction(Intent.ACTION_PACKAGE_REMOVED);
+        }), any(), any());
+
+        receiver.onReceive(mMockContext, new Intent(Intent.ACTION_PACKAGE_ADDED));
+        verify(mSubscriptionTracker).handleSubscriptionsChanged();
+    }
+
+    @Test
+    public void testPackageChangeListener_packageRemoved() throws Exception {
+        final BroadcastReceiver receiver = getPackageChangeReceiver();
+
+        verify(mMockContext).registerReceiver(any(), argThat(filter -> {
+            return filter.hasAction(Intent.ACTION_PACKAGE_REMOVED)
+                    && filter.hasAction(Intent.ACTION_PACKAGE_REMOVED);
+        }), any(), any());
+
+        receiver.onReceive(mMockContext, new Intent(Intent.ACTION_PACKAGE_REMOVED));
+        verify(mSubscriptionTracker).handleSubscriptionsChanged();
+    }
+
+    @Test
     public void testSetVcnConfigRequiresNonSystemServer() throws Exception {
         doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid();
 
@@ -493,7 +544,7 @@
         doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid();
 
         try {
-            mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1);
+            mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, TEST_PACKAGE_NAME);
             fail("Expected IllegalStateException exception for system server");
         } catch (IllegalStateException expected) {
         }
@@ -506,7 +557,7 @@
                 .getBinderCallingUid();
 
         try {
-            mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1);
+            mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, TEST_PACKAGE_NAME);
             fail("Expected security exception for non system user");
         } catch (SecurityException expected) {
         }
@@ -517,15 +568,24 @@
         setupMockedCarrierPrivilege(false);
 
         try {
-            mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1);
+            mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, TEST_PACKAGE_NAME);
             fail("Expected security exception for missing carrier privileges");
         } catch (SecurityException expected) {
         }
     }
 
     @Test
+    public void testClearVcnConfigMismatchedPackages() throws Exception {
+        try {
+            mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, "IncorrectPackage");
+            fail("Expected security exception due to mismatched packages");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    @Test
     public void testClearVcnConfig() throws Exception {
-        mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1);
+        mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, TEST_PACKAGE_NAME);
         assertTrue(mVcnMgmtSvc.getConfigs().isEmpty());
         verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
     }
@@ -536,7 +596,7 @@
         mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_2, mMockStatusCallback, TEST_PACKAGE_NAME);
         verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_ACTIVE);
 
-        mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2);
+        mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2, TEST_PACKAGE_NAME);
 
         verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED);
     }
@@ -565,7 +625,7 @@
         verify(vcnInstance).updateConfig(TEST_VCN_CONFIG);
 
         // Verify Vcn is stopped if it was already started
-        mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2);
+        mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2, TEST_PACKAGE_NAME);
         verify(vcnInstance).teardownAsynchronously();
     }
 
@@ -593,6 +653,16 @@
         mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
     }
 
+    @Test(expected = SecurityException.class)
+    public void testRemoveVcnUnderlyingNetworkPolicyListenerInvalidPermission() {
+        doThrow(new SecurityException())
+                .when(mMockContext)
+                .enforceCallingOrSelfPermission(
+                        eq(android.Manifest.permission.NETWORK_FACTORY), any());
+
+        mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+    }
+
     @Test
     public void testRemoveVcnUnderlyingNetworkPolicyListenerNeverRegistered() {
         mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
@@ -646,7 +716,7 @@
                     .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID));
         } else if (transport == TRANSPORT_WIFI) {
             WifiInfo wifiInfo = mock(WifiInfo.class);
-            when(wifiInfo.makeCopy(anyBoolean())).thenReturn(wifiInfo);
+            when(wifiInfo.makeCopy(anyLong())).thenReturn(wifiInfo);
             when(mMockDeps.getSubIdForWifiInfo(eq(wifiInfo))).thenReturn(TEST_SUBSCRIPTION_ID);
 
             ncBuilder
@@ -772,7 +842,7 @@
         mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
         mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
 
-        mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2);
+        mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2, TEST_PACKAGE_NAME);
 
         verify(mMockPolicyListener).onPolicyChanged();
     }
diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
index 1d459a3..1ef1a61 100644
--- a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
@@ -194,29 +194,35 @@
     }
 
     private NetworkRequest getWifiRequest() {
-        return getExpectedRequestBase()
+        return getExpectedRequestBase(true)
                 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                 .build();
     }
 
     private NetworkRequest getCellRequestForSubId(int subId) {
-        return getExpectedRequestBase()
+        return getExpectedRequestBase(false)
                 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                 .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
                 .build();
     }
 
     private NetworkRequest getRouteSelectionRequest() {
-        return getExpectedRequestBase().build();
+        return getExpectedRequestBase(true).build();
     }
 
-    private NetworkRequest.Builder getExpectedRequestBase() {
-        return new NetworkRequest.Builder()
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
-                .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
-                .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
-                .addUnwantedCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+    private NetworkRequest.Builder getExpectedRequestBase(boolean requireVcnManaged) {
+        final NetworkRequest.Builder builder =
+                new NetworkRequest.Builder()
+                        .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                        .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+                        .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+                        .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+
+        if (requireVcnManaged) {
+            builder.addUnwantedCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+        }
+
+        return builder;
     }
 
     @Test
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 4bf8491..ca6448c 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -73,7 +73,7 @@
 
         mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
 
-        mIkeSession = mGatewayConnection.buildIkeSession();
+        mIkeSession = mGatewayConnection.buildIkeSession(TEST_UNDERLYING_NETWORK_RECORD_1.network);
         mGatewayConnection.setIkeSession(mIkeSession);
 
         mGatewayConnection.transitionTo(mGatewayConnection.mConnectedState);
@@ -188,7 +188,7 @@
                         any(),
                         lpCaptor.capture(),
                         ncCaptor.capture(),
-                        anyInt(),
+                        any(),
                         any(),
                         anyInt());
         verify(mIpSecSvc)
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
index d07d2cf..7afa449 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
@@ -25,12 +25,15 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
+import android.net.ipsec.ike.IkeSessionParams;
+
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 
 /** Tests for VcnGatewayConnection.ConnectingState */
 @RunWith(AndroidJUnit4.class)
@@ -51,7 +54,12 @@
 
     @Test
     public void testEnterStateCreatesNewIkeSession() throws Exception {
-        verify(mDeps).newIkeSession(any(), any(), any(), any(), any());
+        final ArgumentCaptor<IkeSessionParams> paramsCaptor =
+                ArgumentCaptor.forClass(IkeSessionParams.class);
+        verify(mDeps).newIkeSession(any(), paramsCaptor.capture(), any(), any(), any());
+        assertEquals(
+                TEST_UNDERLYING_NETWORK_RECORD_1.network,
+                paramsCaptor.getValue().getConfiguredNetwork());
     }
 
     @Test
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
index 661e03a..99feffd 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
@@ -38,7 +38,8 @@
     public void setUp() throws Exception {
         super.setUp();
 
-        mGatewayConnection.setIkeSession(mGatewayConnection.buildIkeSession());
+        mGatewayConnection.setIkeSession(
+                mGatewayConnection.buildIkeSession(TEST_UNDERLYING_NETWORK_RECORD_2.network));
 
         // ensure that mGatewayConnection has an underlying Network before entering
         // DisconnectingState
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index 748c792..d08af9d 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -18,6 +18,7 @@
 
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
@@ -87,6 +88,7 @@
     private void verifyBuildNetworkCapabilitiesCommon(int transportType) {
         final NetworkCapabilities underlyingCaps = new NetworkCapabilities();
         underlyingCaps.addTransportType(transportType);
+        underlyingCaps.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         underlyingCaps.addCapability(NET_CAPABILITY_NOT_METERED);
         underlyingCaps.addCapability(NET_CAPABILITY_NOT_ROAMING);
 
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
index 4fa63d4..c853fc5 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -29,6 +29,7 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -139,8 +140,7 @@
         mTestLooper.dispatchAll();
     }
 
-    @Test
-    public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() {
+    private void verifyUpdateSubscriptionSnapshotNotifiesConnectionGateways(boolean isActive) {
         final NetworkRequestListener requestListener = verifyAndGetRequestListener();
         startVcnGatewayWithCapabilities(requestListener, TEST_CAPS[0]);
 
@@ -150,14 +150,27 @@
         final TelephonySubscriptionSnapshot updatedSnapshot =
                 mock(TelephonySubscriptionSnapshot.class);
 
+        mVcn.setIsActive(isActive);
+
         mVcn.updateSubscriptionSnapshot(updatedSnapshot);
         mTestLooper.dispatchAll();
 
         for (final VcnGatewayConnection gateway : gatewayConnections) {
-            verify(gateway).updateSubscriptionSnapshot(eq(updatedSnapshot));
+            verify(gateway, isActive ? times(1) : never())
+                    .updateSubscriptionSnapshot(eq(updatedSnapshot));
         }
     }
 
+    @Test
+    public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() {
+        verifyUpdateSubscriptionSnapshotNotifiesConnectionGateways(true /* isActive */);
+    }
+
+    @Test
+    public void testSubscriptionSnapshotUpdatesVcnGatewayConnectionsWhileInactive() {
+        verifyUpdateSubscriptionSnapshotNotifiesConnectionGateways(false /* isActive */);
+    }
+
     private void triggerVcnRequestListeners(NetworkRequestListener requestListener) {
         for (final int[] caps : TEST_CAPS) {
             startVcnGatewayWithCapabilities(requestListener, caps);
@@ -187,7 +200,6 @@
             NetworkRequestListener requestListener,
             Set<VcnGatewayConnection> expectedGatewaysTornDown) {
         assertFalse(mVcn.isActive());
-        assertTrue(mVcn.getVcnGatewayConnections().isEmpty());
         for (final VcnGatewayConnection gatewayConnection : expectedGatewaysTornDown) {
             verify(gatewayConnection).teardownAsynchronously();
         }
@@ -238,6 +250,51 @@
     }
 
     @Test
+    public void testGatewayQuitWhileInactive() {
+        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+        final Set<VcnGatewayConnection> gatewayConnections =
+                new ArraySet<>(startGatewaysAndGetGatewayConnections(requestListener));
+
+        mVcn.teardownAsynchronously();
+        mTestLooper.dispatchAll();
+
+        final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
+        statusCallback.onQuit();
+        mTestLooper.dispatchAll();
+
+        // Verify that the VCN requests the networkRequests be resent
+        assertEquals(1, mVcn.getVcnGatewayConnections().size());
+        verify(mVcnNetworkProvider, never()).resendAllRequests(requestListener);
+    }
+
+    @Test
+    public void testUpdateConfigReevaluatesGatewayConnections() {
+        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+        startGatewaysAndGetGatewayConnections(requestListener);
+        assertEquals(2, mVcn.getVcnGatewayConnectionConfigMap().size());
+
+        // Create VcnConfig with only one VcnGatewayConnectionConfig so a gateway connection is torn
+        // down
+        final VcnGatewayConnectionConfig activeConfig =
+                VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(TEST_CAPS[0]);
+        final VcnGatewayConnectionConfig removedConfig =
+                VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(TEST_CAPS[1]);
+        final VcnConfig updatedConfig =
+                new VcnConfig.Builder(mContext).addGatewayConnectionConfig(activeConfig).build();
+
+        mVcn.updateConfig(updatedConfig);
+        mTestLooper.dispatchAll();
+
+        final VcnGatewayConnection activeGatewayConnection =
+                mVcn.getVcnGatewayConnectionConfigMap().get(activeConfig);
+        final VcnGatewayConnection removedGatewayConnection =
+                mVcn.getVcnGatewayConnectionConfigMap().get(removedConfig);
+        verify(activeGatewayConnection, never()).teardownAsynchronously();
+        verify(removedGatewayConnection).teardownAsynchronously();
+        verify(mVcnNetworkProvider).resendAllRequests(requestListener);
+    }
+
+    @Test
     public void testUpdateConfigExitsSafeMode() {
         final NetworkRequestListener requestListener = verifyAndGetRequestListener();
         final Set<VcnGatewayConnection> gatewayConnections =
@@ -261,8 +318,8 @@
         verify(mVcnNetworkProvider, times(2)).registerListener(eq(requestListener));
         assertTrue(mVcn.isActive());
         for (final int[] caps : TEST_CAPS) {
-            // Expect each gateway connection created on initial startup, and again with new configs
-            verify(mDeps, times(2))
+            // Expect each gateway connection created only on initial startup
+            verify(mDeps)
                     .newVcnGatewayConnection(
                             eq(mVcnContext),
                             eq(TEST_SUB_GROUP),
@@ -271,4 +328,14 @@
                             any());
         }
     }
+
+    @Test
+    public void testIgnoreNetworkRequestWhileInactive() {
+        mVcn.setIsActive(false /* isActive */);
+
+        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+        triggerVcnRequestListeners(requestListener);
+
+        verify(mDeps, never()).newVcnGatewayConnection(any(), any(), any(), any(), any());
+    }
 }